diff --git a/Packages/PostgresDAC10.dpk b/Packages/PostgresDAC10.dpk index 4ec28e4..aa2b105 100644 --- a/Packages/PostgresDAC10.dpk +++ b/Packages/PostgresDAC10.dpk @@ -35,19 +35,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC11.dpk b/Packages/PostgresDAC11.dpk index ca4b671..42f216b 100644 --- a/Packages/PostgresDAC11.dpk +++ b/Packages/PostgresDAC11.dpk @@ -36,19 +36,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC12.dpk b/Packages/PostgresDAC12.dpk index 336d647..9b342d7 100644 --- a/Packages/PostgresDAC12.dpk +++ b/Packages/PostgresDAC12.dpk @@ -36,19 +36,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC14.dpk b/Packages/PostgresDAC14.dpk index a32be7c..2cee46b 100644 --- a/Packages/PostgresDAC14.dpk +++ b/Packages/PostgresDAC14.dpk @@ -36,19 +36,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC15.dpk b/Packages/PostgresDAC15.dpk index 4c9df58..47ef363 100644 --- a/Packages/PostgresDAC15.dpk +++ b/Packages/PostgresDAC15.dpk @@ -36,20 +36,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC16.dpk b/Packages/PostgresDAC16.dpk index bbc2442..5c69ef5 100644 --- a/Packages/PostgresDAC16.dpk +++ b/Packages/PostgresDAC16.dpk @@ -34,19 +34,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC17.dpk b/Packages/PostgresDAC17.dpk index ee7576b..4d9a3ed 100644 --- a/Packages/PostgresDAC17.dpk +++ b/Packages/PostgresDAC17.dpk @@ -35,19 +35,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC18.dpk b/Packages/PostgresDAC18.dpk index e83f3b5..04540f3 100644 --- a/Packages/PostgresDAC18.dpk +++ b/Packages/PostgresDAC18.dpk @@ -35,19 +35,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC19.dpk b/Packages/PostgresDAC19.dpk index 622ad75..efe47e9 100644 --- a/Packages/PostgresDAC19.dpk +++ b/Packages/PostgresDAC19.dpk @@ -35,19 +35,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC20.dpk b/Packages/PostgresDAC20.dpk index 665e1d8..1eeb572 100644 --- a/Packages/PostgresDAC20.dpk +++ b/Packages/PostgresDAC20.dpk @@ -34,19 +34,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC21.dpk b/Packages/PostgresDAC21.dpk index 3472d6e..59d5bf4 100644 --- a/Packages/PostgresDAC21.dpk +++ b/Packages/PostgresDAC21.dpk @@ -34,19 +34,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC22.dpk b/Packages/PostgresDAC22.dpk index 5e3df3f..9a1cd24 100644 --- a/Packages/PostgresDAC22.dpk +++ b/Packages/PostgresDAC22.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} end. diff --git a/Packages/PostgresDAC23.dpk b/Packages/PostgresDAC23.dpk index 1784006..35d9d38 100644 --- a/Packages/PostgresDAC23.dpk +++ b/Packages/PostgresDAC23.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} end. diff --git a/Packages/PostgresDAC24.dpk b/Packages/PostgresDAC24.dpk index 8217e0b..2b72cd0 100644 --- a/Packages/PostgresDAC24.dpk +++ b/Packages/PostgresDAC24.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} diff --git a/Packages/PostgresDAC25.dpk b/Packages/PostgresDAC25.dpk index b363e8c..9943168 100644 --- a/Packages/PostgresDAC25.dpk +++ b/Packages/PostgresDAC25.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} diff --git a/Packages/PostgresDAC26.dpk b/Packages/PostgresDAC26.dpk index c014c8c..0e71fce 100644 --- a/Packages/PostgresDAC26.dpk +++ b/Packages/PostgresDAC26.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} diff --git a/Packages/PostgresDAC27.dpk b/Packages/PostgresDAC27.dpk index 540d208..fab9e61 100644 --- a/Packages/PostgresDAC27.dpk +++ b/Packages/PostgresDAC27.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} diff --git a/Packages/PostgresDAC28.dpk b/Packages/PostgresDAC28.dpk index e4624a0..de36d2f 100644 --- a/Packages/PostgresDAC28.dpk +++ b/Packages/PostgresDAC28.dpk @@ -34,20 +34,20 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - PSQLBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas', - PSQLGeomTypes in '..\PSQLGeomTypes.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + PSQLBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas', + PSQLGeomTypes in '..\Source\PSQLGeomTypes.pas'; {$NOINCLUDE PSQLGeomTypes} diff --git a/Packages/PostgresDAC5.dpk b/Packages/PostgresDAC5.dpk index 626cee9..f4f1275 100644 --- a/Packages/PostgresDAC5.dpk +++ b/Packages/PostgresDAC5.dpk @@ -31,19 +31,19 @@ requires VCLDB50; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC6.dpk b/Packages/PostgresDAC6.dpk index 8550f8c..6fefa74 100644 --- a/Packages/PostgresDAC6.dpk +++ b/Packages/PostgresDAC6.dpk @@ -32,19 +32,19 @@ requires bdertl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC7.dpk b/Packages/PostgresDAC7.dpk index 80a558a..d84c7ce 100644 --- a/Packages/PostgresDAC7.dpk +++ b/Packages/PostgresDAC7.dpk @@ -34,19 +34,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\PSQLBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\PSQLBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/PostgresDAC9.dpk b/Packages/PostgresDAC9.dpk index 369f7e5..19b44e3 100644 --- a/Packages/PostgresDAC9.dpk +++ b/Packages/PostgresDAC9.dpk @@ -34,19 +34,19 @@ requires dbrtl; contains - PSQLTypes in '..\PSQLTypes.pas', - PSQLAccess in '..\PSQLAccess.pas', - PSQLDbTables in '..\PSQLDbTables.pas', - psqlBatch in '..\psqlBatch.pas', - PSQLMacroQuery in '..\PSQLMacroQuery.pas', - PSQLMonitor in '..\PSQLMonitor.pas', - PSQLTools in '..\PSQLTools.pas', - PSQLDump in '..\PSQLDump.pas', - PSQLCommon in '..\PSQLCommon.pas', - PSQLCopy in '..\PSQLCopy.pas', - PSQLDirectQuery in '..\PSQLDirectQuery.pas', - PSQLExtMask in '..\PSQLExtMask.pas', - PSQLFields in '..\PSQLFields.pas', - PSQLNotify in '..\PSQLNotify.pas'; + PSQLTypes in '..\Source\PSQLTypes.pas', + PSQLAccess in '..\Source\PSQLAccess.pas', + PSQLDbTables in '..\Source\PSQLDbTables.pas', + psqlBatch in '..\Source\psqlBatch.pas', + PSQLMacroQuery in '..\Source\PSQLMacroQuery.pas', + PSQLMonitor in '..\Source\PSQLMonitor.pas', + PSQLTools in '..\Source\PSQLTools.pas', + PSQLDump in '..\Source\PSQLDump.pas', + PSQLCommon in '..\Source\PSQLCommon.pas', + PSQLCopy in '..\Source\PSQLCopy.pas', + PSQLDirectQuery in '..\Source\PSQLDirectQuery.pas', + PSQLExtMask in '..\Source\PSQLExtMask.pas', + PSQLFields in '..\Source\PSQLFields.pas', + PSQLNotify in '..\Source\PSQLNotify.pas'; end. diff --git a/Packages/dclPostgresDAC10.dpk b/Packages/dclPostgresDAC10.dpk index 6423fb7..f0a95ac 100644 --- a/Packages/dclPostgresDAC10.dpk +++ b/Packages/dclPostgresDAC10.dpk @@ -31,13 +31,13 @@ requires PostgresDAC10; contains - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC11.dpk b/Packages/dclPostgresDAC11.dpk index 2749365..3679190 100644 --- a/Packages/dclPostgresDAC11.dpk +++ b/Packages/dclPostgresDAC11.dpk @@ -31,13 +31,13 @@ requires PostgresDAC11; contains - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC12.dpk b/Packages/dclPostgresDAC12.dpk index cc6af6f..8a3d004 100644 --- a/Packages/dclPostgresDAC12.dpk +++ b/Packages/dclPostgresDAC12.dpk @@ -31,13 +31,13 @@ requires dcldb; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC14.dpk b/Packages/dclPostgresDAC14.dpk index 31c6694..a93070c 100644 --- a/Packages/dclPostgresDAC14.dpk +++ b/Packages/dclPostgresDAC14.dpk @@ -31,13 +31,13 @@ requires PostgresDAC14; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC15.dpk b/Packages/dclPostgresDAC15.dpk index 4ccbc7b..21370d6 100644 --- a/Packages/dclPostgresDAC15.dpk +++ b/Packages/dclPostgresDAC15.dpk @@ -32,13 +32,13 @@ requires PostgresDAC15; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC16.dpk b/Packages/dclPostgresDAC16.dpk index ad7d36d..2965d63 100644 --- a/Packages/dclPostgresDAC16.dpk +++ b/Packages/dclPostgresDAC16.dpk @@ -34,14 +34,14 @@ requires PostgresDAC16; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC17.dpk b/Packages/dclPostgresDAC17.dpk index 75657f4..73e8e25 100644 --- a/Packages/dclPostgresDAC17.dpk +++ b/Packages/dclPostgresDAC17.dpk @@ -34,14 +34,14 @@ requires PostgresDAC17; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC18.dpk b/Packages/dclPostgresDAC18.dpk index d971ff0..bd20df1 100644 --- a/Packages/dclPostgresDAC18.dpk +++ b/Packages/dclPostgresDAC18.dpk @@ -34,14 +34,14 @@ requires PostgresDAC18; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC19.dpk b/Packages/dclPostgresDAC19.dpk index f86c6ed..b73fc24 100644 --- a/Packages/dclPostgresDAC19.dpk +++ b/Packages/dclPostgresDAC19.dpk @@ -34,14 +34,14 @@ requires PostgresDAC19; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC20.dpk b/Packages/dclPostgresDAC20.dpk index 69269b2..6a2555d 100644 --- a/Packages/dclPostgresDAC20.dpk +++ b/Packages/dclPostgresDAC20.dpk @@ -34,14 +34,14 @@ requires PostgresDAC20; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC21.dpk b/Packages/dclPostgresDAC21.dpk index 1d06c0c..8518458 100644 --- a/Packages/dclPostgresDAC21.dpk +++ b/Packages/dclPostgresDAC21.dpk @@ -34,14 +34,14 @@ requires PostgresDAC21; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC22.dpk b/Packages/dclPostgresDAC22.dpk index c369ec8..0dc7ad4 100644 --- a/Packages/dclPostgresDAC22.dpk +++ b/Packages/dclPostgresDAC22.dpk @@ -34,14 +34,14 @@ requires PostgresDAC22; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC23.dpk b/Packages/dclPostgresDAC23.dpk index 047936e..65f996c 100644 --- a/Packages/dclPostgresDAC23.dpk +++ b/Packages/dclPostgresDAC23.dpk @@ -34,14 +34,14 @@ requires PostgresDAC23; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC24.dpk b/Packages/dclPostgresDAC24.dpk index 9649e9b..82626bf 100644 --- a/Packages/dclPostgresDAC24.dpk +++ b/Packages/dclPostgresDAC24.dpk @@ -34,14 +34,14 @@ requires PostgresDAC24; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC25.dpk b/Packages/dclPostgresDAC25.dpk index 2a05763..c9e5e6e 100644 --- a/Packages/dclPostgresDAC25.dpk +++ b/Packages/dclPostgresDAC25.dpk @@ -34,14 +34,14 @@ requires PostgresDAC25; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC26.dpk b/Packages/dclPostgresDAC26.dpk index def1772..39c96d7 100644 --- a/Packages/dclPostgresDAC26.dpk +++ b/Packages/dclPostgresDAC26.dpk @@ -34,14 +34,14 @@ requires PostgresDAC26; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC27.dpk b/Packages/dclPostgresDAC27.dpk index 32f0998..61c0cd6 100644 --- a/Packages/dclPostgresDAC27.dpk +++ b/Packages/dclPostgresDAC27.dpk @@ -34,14 +34,14 @@ requires PostgresDAC27; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC28.dpk b/Packages/dclPostgresDAC28.dpk index 4f05323..b9bb61b 100644 --- a/Packages/dclPostgresDAC28.dpk +++ b/Packages/dclPostgresDAC28.dpk @@ -34,14 +34,14 @@ requires PostgresDAC28; contains - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}; + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}; end. diff --git a/Packages/dclPostgresDAC5.dpk b/Packages/dclPostgresDAC5.dpk index 21c7525..7ac5e6b 100644 --- a/Packages/dclPostgresDAC5.dpk +++ b/Packages/dclPostgresDAC5.dpk @@ -31,13 +31,13 @@ requires dcldb50; contains - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC6.dpk b/Packages/dclPostgresDAC6.dpk index 21242c4..59266ff 100644 --- a/Packages/dclPostgresDAC6.dpk +++ b/Packages/dclPostgresDAC6.dpk @@ -31,13 +31,13 @@ requires dcldb; contains - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC7.dpk b/Packages/dclPostgresDAC7.dpk index 11d1742..df1d93a 100644 --- a/Packages/dclPostgresDAC7.dpk +++ b/Packages/dclPostgresDAC7.dpk @@ -31,13 +31,13 @@ requires dcldb; contains - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/Packages/dclPostgresDAC9.dpk b/Packages/dclPostgresDAC9.dpk index 1fcdd26..1e6e1c9 100644 --- a/Packages/dclPostgresDAC9.dpk +++ b/Packages/dclPostgresDAC9.dpk @@ -31,13 +31,13 @@ requires dcldb; contains - PSQLStoredProcFrm in '..\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, - PSQLupdsqled in '..\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, - psqlAboutFrm in '..\psqlAboutFrm.pas' {PSQLAboutComp}, - PSQLCOMP in '..\PSQLCOMP.pas', - PSQLConnFrm in '..\PSQLConnFrm.pas' {PSQLConnForm}, - PSQLfldlinks in '..\PSQLfldlinks.pas' {PSQLLinkFields}, - PSQLMigrator in '..\PSQLMigrator.pas', - PSQLQueryEdit in '..\PSQLQueryEdit.pas' {SQLEditForm}; + PSQLStoredProcFrm in '..\Source\PSQLStoredProcFrm.pas' {PSQLStoredProcProp}, + PSQLupdsqled in '..\Source\PSQLupdsqled.pas' {PSQLUpdateSQLEditForm}, + psqlAboutFrm in '..\Source\psqlAboutFrm.pas' {PSQLAboutComp}, + PSQLCOMP in '..\Source\PSQLCOMP.pas', + PSQLConnFrm in '..\Source\PSQLConnFrm.pas' {PSQLConnForm}, + PSQLfldlinks in '..\Source\PSQLfldlinks.pas' {PSQLLinkFields}, + PSQLMigrator in '..\Source\PSQLMigrator.pas', + PSQLQueryEdit in '..\Source\PSQLQueryEdit.pas' {SQLEditForm}; end. diff --git a/PDAC.DCR b/Source/PDAC.DCR similarity index 100% rename from PDAC.DCR rename to Source/PDAC.DCR diff --git a/PDAC.ico b/Source/PDAC.ico similarity index 100% rename from PDAC.ico rename to Source/PDAC.ico diff --git a/PSQLAccess.pas b/Source/PSQLAccess.pas similarity index 96% rename from PSQLAccess.pas rename to Source/PSQLAccess.pas index ef12228..3d45595 100644 --- a/PSQLAccess.pas +++ b/Source/PSQLAccess.pas @@ -1,10302 +1,10302 @@ -{$I pSQLDAC.inc} - -unit PSQLAccess; - -{SVN revision: $Id$} - -{$T-} - -interface - -uses Classes, Db, PSQLTypes, Math, - {$IFDEF FPC}FMTBcd, LCLIntf,{$ENDIF} - {$IFDEF DELPHI_12}PSQLGeomTypes, {$ENDIF} - {$IFDEF DELPHI_9}DbCommon,{$ELSE}PSQLCommon,{$ENDIF} - {$IFDEF DELPHI_6}FmtBcd, Variants,{$ENDIF} - {$IFDEF FPC}Variants,{$ENDIF} - {$IFDEF NEXTGEN}Generics.Collections,{$ENDIF} - SysUtils; - -{$IFDEF DELPHI_12} - {$NOINCLUDE PSQLGeomTypes} -{$ENDIF} - -type - {$IFDEF NEXTGEN} - TPListObject = class - private - FValue: Integer; - public - class operator Implicit(obj: TPListObject): Integer; - constructor Create(Value: Integer); overload; - end; - {$ENDIF} - - {Forward declaration} - TNativeConnect = class; - -{****************************************************************************} -{ Error handler } -{****************************************************************************} - EPSQLException = Class(EAbort) - private - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FPSQL : TNativeConnect; - FPSQLErrorCode : Word; - FBDEErrorCode : Word; - FBDE : Boolean; - FPSQLErrorMsg : String; - public - constructor CreateBDE(ECode : Word); - constructor CreateBDEMsg(ECode : Word; Const EMessage : String); - constructor Create(PSQL : TNativeConnect); - constructor CreateMsg(PSQL : TNativeConnect; Const ErrorMsg : String ); - property PSQLErrorCode : word read FPSQLErrorCode; - property PSQLErrorMsg : String read FPSQLErrorMsg; - property BDEErrorCode : Word read FBDEErrorCode; - property BDEErrors : Boolean read FBDE; - end; - -{****************************************************************************} -{ TNativeConnect } -{****************************************************************************} - TNativeConnect = Class(TObject) - private - FHandle : PPGconn; - FSystem : Boolean; - FLastOperationTime : cardinal; - FBlobTransactionInProgress: boolean; - FUnicodeUsed : boolean; - FCharset : string; - FServerVersion : string; - FIntServerVersion : integer; - FConnectString : string; - FDirectConnectString : string; - FTransState : eXState; { Transaction end control xsActive, xsInactive } - FTransLevel : eXILType; { Transaction isolation levels } - FGUCList : TStrings; {GUC parameters as key=value list} - function GetBackendPID : integer; - function IsTransactionActive: boolean; - function GetTransactionStatus: TTransactionStatusType; - function GetNativeByteaFormat: TNativeByteaFormat; - public - FErrorPos : string; - FErrorContext : string; - FErrorSeverity : string; - FErrorSQLState : string; - FErrorDetail : string; - FErrorPrimary : string; - FErrorHint : string; - FErrorInternalPos : string; - FErrorInternalQuery : string; - FErrorSourceFile : string; - FErrorSourceLine : string; - FErrorSourceFunc : string; - FErrorSchemaName : string; - FErrorTableName : string; - FErrorColumnName : string; - FErrorDataTypeName : string; - FErrorConstraintName : string; - - FLoggin : Boolean; {Loggin flag} - DBOptions : TDBOptions; {Connection parameters} - constructor Create; - destructor Destroy; Override; - - class function Ping(Params: TStrings): TPingStatus; - - procedure DirectExecute(SQL: String); - procedure ProcessDBParams(Params : TStrings); - procedure InternalConnect(ConnParams: TStrings = nil); {Login to database} - procedure InternalDisconnect; {Logout from database} - procedure ReloadGUC; - procedure Reset; {reset connection to server} - function Rollback: boolean; {Rollback transaction} - function Commit: boolean; {Commit transaction} - procedure CancelBackend(PID: Integer); - procedure CheckResult; overload;{Check result last operation} - procedure CheckResult(FStatement: PPGresult); overload; - function GetErrorText: String; {Get Error text} - function Success: Boolean; - procedure StoredProcParams(pszPName: string; ProcOID: cardinal; - List: TList{$IFDEF NEXTGEN}{$ENDIF}); - procedure GUCList(List : TStrings); - procedure StoredProcList(pszWild : string; List : TStrings); - procedure TableList(pszWild : string; SystemTables: Boolean; List : TStrings); - procedure UserList(pszWild : string; List : TStrings); - procedure SchemaList(pszWild : string; SystemSchemas: Boolean; List : TStrings); - procedure TablespaceList(pszWild : string; List : TStrings); - procedure DatabaseList(pszWild : string; List : TStrings); - procedure OpenTable(pszTableName: string; pszIndexName: string; iIndexId: Word; - eOpenMode: DBIOpenMode;eShareMode: DBIShareMode;var hCursor: hDBICur; - AnOptions: TPSQLDatasetOptions; - Limit, Offset : Integer); - procedure QueryAlloc(var hStmt: hDBIStmt); - procedure QueryPrepare(var hStmt: hDBIStmt;Query : String); - procedure BeginTran(eXIL: eXILType; var hXact: hDBIXact); - procedure BeginBLOBTran; - procedure RollbackBLOBTran; - procedure CommitBLOBTran; - procedure EndTran(hXact : hDBIXact; eEnd : eXEnd); - procedure GetTranInfo(hXact : hDBIXact; pxInfo : pXInfo); - procedure QExecDirect(pszQuery : String; phCur: phDBICur; var AffectedRows : integer); - procedure OpenFieldList(pszTableName: string; pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur); - procedure OpenIndexList(pszTableName: string; pszDriverType: string; var hCur: hDBICur); - function GetCharSet: string; - procedure GetCharSetList(var List: TStrings); - procedure SetCharSet(var ACharSet: string); - function GetTimeout: cardinal; - function SetTimeout(const Timeout: cardinal): cardinal; - procedure SetErrorVerbosity(const ErrorVerbosity: TErrorVerbosity); - function GetServerVersion: string; - function GetserverVersionAsInt: integer; - procedure GetUserProps(const UserName: string; var SuperUser, CanCreateDB, - CanUpdateSysCatalogs: boolean; var UserID: integer; - var ValidUntil: string); - procedure GetDBProps(const DB: string; - var Owner, Tablespace: string; - var IsTemplate: boolean; - var DBOid: cardinal; var Comment: string); - procedure GetTableProps(const TableName: string; - var Owner, Comment, Tablespace: string; - var TableOid: cardinal); - procedure EmptyTable(hCursor : hDBICur; pszTableName : string); - procedure AddIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; var IdxDesc: IDXDesc; pszKeyviolName: string); - procedure DeleteIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; pszIndexName: string; pszIndexTagName: string; iIndexId: Word); - - property IsolationLevel : eXILType Read FTransLevel; - property Handle : PPGconn read FHandle write FHandle; - property BackendPID : Integer read GetBackendPID; - property LastOperationTime: cardinal read FLastOperationTime; - property InTransaction: boolean read IsTransactionActive; - property TransactionStatus: TTransactionStatusType read GetTransactionStatus; - property BlobTransactionInProgress: boolean read FBlobTransactionInProgress; - property NativeByteaFormat: TNativeByteaFormat read GetNativeByteaFormat; - property IsUnicodeUsed: boolean read FUnicodeUsed; - property GUC: TStrings read FGUCList; - - function SelectStringDirect(pszQuery : string; var IsOk : boolean; aFieldNumber : integer):string; overload; - function SelectStringDirect(pszQuery : string; var IsOk : boolean; pszFieldName : string):string; overload; - function SelectStringsDirect(pszQuery : string; aList : TStrings; aFieldNumber : integer):string; overload; - function SelectStringsDirect(pszQuery : string; aList : TStrings; pszFieldName : string):string; overload; - - - function IsSSLUsed: boolean; - - function RawToString(S: PAnsiDACChar): string; - function StringToRaw(S: string): PAnsiDACChar; //need to be free by StrDispose - function StringToRawS(S: string): DACAString; -{$IFDEF DELPHI_15} - function BinaryToString(S: PAnsiDACChar; TypeOID: cardinal): string; -{$ENDIF} - end; - - DAChDBIDb = {$IFNDEF NEXTGEN}hDBIDb{$ELSE}TNativeConnect{$ENDIF}; - - {Postgres Engine} - TPSQLEngine = Class(TBaseObject) - private - FDatabase : DAChDBIDb; - FNativeStatus : Integer; - FNativeMsg : string; - function GetDatabase: DAChDBIDb; - procedure SetDatabase(H : DAChDBIDb); - public - constructor Create(P : TObject; Container : TContainer); - Destructor Destroy; Override; - property Status: Integer Read FNativeStatus; - property MessageStatus : String read FNativeMsg; - property Database: DAChDBIDb Read GetDatabase Write SetDatabase; - function IsSqlBased(hDb: DAChDBIDb): Boolean; - function Ping(Params: TStrings; var PingResult: TPingStatus): DBIResult; - function OpenDatabase(Params : TStrings; UseSinleLineConnInfo: boolean; var hDb: DAChDBIDb): DBIResult; - function CloseDatabase(var hDb : DAChDBIDb) : DBIResult; - function OpenTable(hDb: DAChDBIDb; pszTableName: string; pszIndexName: string; iIndexId: Word; eOpenMode: DBIOpenMode; - eShareMode: DBIShareMode; var hCursor: hDBICur; AnOptions: TPSQLDatasetOptions; - Limit, Offset : Integer): DBIResult; - function OpenStoredProcParams(hDb: DAChDBIDb;pszPName: string; ProcOID:cardinal; - List : TList{$IFDEF NEXTGEN}{$ENDIF}): DBIResult; - function OpenStoredProcList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; - function OpenTableList(hDb: DAChDBIDb; pszWild: string; SystemTables: Boolean; List : TStrings): DBIResult; - function OpenUserList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; - function OpenSchemaList(hDb: DAChDBIDb; pszWild: string; SystemSchemas: Boolean; List : TStrings): DBIResult; - function OpenTablespaceList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; - function SetToBookMark(hCur: hDBICur; pBookMark : Pointer) : DBIResult; - function CompareBookMarks(hCur: hDBICur; pBookMark1, pBookMark2 : Pointer;var CmpBkmkResult : CmpBkmkRslt): DBIResult; - function GetNextRecord(hCursor: hDBICur;eLock: DBILockType;pRecBuff: Pointer;pRecProps: pRECProps): DBIResult; - function CloseCursor(hCursor: hDBICur): DBIResult; - function PutField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pSrc: Pointer): DBIResult; - function OpenBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode): DBIResult; - function GetBlobSize(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;var iSize: integer): DBIResult; - function GetBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint;pDest: Pointer;var iRead: integer): DBIResult; - function PutBlob(hCursor : hDBICur; PRecord : Pointer; FieldNo : Word; iOffSet : Longint; iLen : Longint; pSrc : Pointer): DBIResult; - function TruncateBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iLen: Longint): DBIResult; - function FreeBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word): DBIResult; - function CloseBlob(hCursor: hDBICur; FieldNo: Word): DBIResult; - function BeginTran(hDb: DAChDBIDb; eXIL: eXILType; var hXact: hDBIXact): DBIResult; - function EndTran(hDb: DAChDBIDb; hXact: hDBIXact; eEnd : eXEnd): DBIResult; - function GetTranInfo(hDb: DAChDBIDb;hXact: hDBIXact; pxInfo: pXInfo): DBIResult; - function GetTranStatus(hDb: DAChDBIDb; var TranStatus: TTransactionStatusType): DBIResult; - function GetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Pointer;iMaxLen: integer; var iLen: integer): DBIResult; - function SetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Longint): DBIResult; - function GetVchkDesc(hCursor: hDBICur;iValSeqNo: Word; var pvalDesc: VCHKDesc): DBIResult; - function GetCursorProps(hCursor: hDBICur;var curProps: CURProps): DBIResult; - function GetFieldDescs(hCursor: hDBICur; var pfldDesc: TFLDDescList): DBIResult; - function SetToBegin(hCursor: hDBICur): DBIResult; - function SetToEnd(hCursor: hDBICur): DBIResult; - function RelRecordLock(hCursor: hDBICur;bAll: Boolean): DBIResult; - function ReadBlock(hCursor: hDBICur; var iRecords: Integer; pBuf: Pointer): DBIResult; - function InitRecord(hCursor: hDBICur;PRecord: Pointer ): DBIResult; - function InsertRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer): DBIResult; - function AppendRecord(hCursor: hDBICur;PRecord:Pointer): DBIResult; - function ModifyRecord(hCursor: hDBICur;OldRecord,PRecord:Pointer;bFreeLock: Boolean;ARecno: LongInt): DBIResult; - function DeleteRecord(hCursor: hDBICur;PRecord:Pointer): DBIResult; - function SettoSeqNo(hCursor: hDBICur;iSeqNo: Longint): DBIResult; - function GetPriorRecord(hCursor: hDBICur;eLock:DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; - function GetRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; - function GetBookMark(hCur: hDBICur;pBookMark: Pointer): DBIResult; - function GetRecordCount(hCursor: hDBICur;Var iRecCount: integer): DBIResult; - function ForceReread(hCursor: hDBICur): DBIResult; - function GetField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pDest: Pointer;var bBlank: Boolean): DBIResult; - function AddFilter(hCursor: hDBICur;iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr;pfFilter: pfGENFilter;var hFilter: hDBIFilter): DBIResult; - function DropFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; - function ActivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; - function DeactivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; - function GetErrorString(rslt: DBIResult; var ErrorMsg: String): DBIResult; - function QExecDirect(hDb: DAChDBIDb; pszQuery: String;phCur: phDBICur; var AffectedRows : integer): DBIResult; - function QAlloc(hDb: DAChDBIDb;var hStmt: hDBIStmt): DBIResult; - function QPrepare(hStmt: hDBIStmt; pszQuery: String): DBIResult; - function QExec(hStmt: hDBIStmt; phCur: phDBICur; var AffectedRows: integer): DBIResult; - function QFree(var hStmt: hDBIStmt): DBIResult; - function QPrepareProc (hDb: DAChDBIDb; pszProc: PChar; hParams: pointer; var hStmt: hDBIStmt): DBIResult; - function QSetProcParams (hStmt: hDBIStmt; Params: TParams): DBIResult; - function QGetProcParams (hStmt: hDBIStmt; Params: TParams): DBIResult; - function QuerySetParams(hStmt: hDBIStmt;Params : TParams; SQLText : String): DBIResult; - function CheckError : DBIResult; - function GetDatabases(hDb: DAChDBIDb; pszWild: string; List : TStrings):DBIResult; - function GetCharacterSet(hDb : DAChDBIDb; var CharSet : string):DBIResult; - function GetCharacterSets(hDb : DAChDBIDb; List: TStrings):DBIResult; - function SetCharacterSet(hDb : DAChDBIDb; var CharSet : string): DBIResult; - function SetErrorVerbosity(hDb : DAChDBIDb; const ErrorVerbosity: TErrorVerbosity): DBIResult; - function GetCommandTimeout(hDb : DAChDBIDb; var Timeout : cardinal): DBIResult; - function SetCommandTimeout(hDb : DAChDBIDb; var Timeout : cardinal): DBIResult; - function OpenFieldList(hDb: DAChDBIDb; pszTableName: string; pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur): DBIResult; - function OpenIndexList(hDb: DAChDBIDb; pszTableName: string; pszDriverType: string; var hCur: hDBICur): DBIResult; - function EmptyTable(hDb: DAChDBIDb; hCursor : hDBICur; pszTableName : string; pszDriverType : string): DBIResult; - function SetRange(hCursor : hDBICur;bKeyItself: Boolean;iFields1: Word;iLen1: Word;pKey1: Pointer;bKey1Incl: Boolean; - iFields2: Word;iLen2: Word;pKey2: Pointer;bKey2Incl: Boolean): DBIResult; - function ResetRange(hCursor : hDBICur) : DBIResult; - function SwitchToIndex(hCursor : hDBICur; pszIndexName, pszTagName : string; iIndexId : Word; bCurrRec : Boolean) : DBIResult; - function ExtractKey(hCursor: hDBICur;PRecord: Pointer;pKeyBuf: Pointer): DBIResult; - function GetRecordForKey(hCursor: hDBICur; bDirectKey: Boolean; iFields: integer; iLen: integer; pKey: Pointer; pRecBuff: Pointer; AStrictConformity: boolean = False): DBIResult; - function AddIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;var IdxDesc: IDXDesc;pszKeyviolName: string): DBIResult; - function DeleteIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;pszIndexName: string;pszIndexTagName: string;iIndexId: Word): DBIResult; - function GetIndexDesc(hCursor: hDBICur;iIndexSeqNo: Word;var idxDesc: IDXDesc): DBIResult; - function GetIndexDescs(hCursor: hDBICur; idxDescs: TIDXDescList): DBIResult; - function TranslateRecordStructure(pszSrcDriverType: PChar; iFlds: Word; pfldsSrc: pFLDDesc; pszDstDriverType: PChar; pszLangDriver: PChar;pfldsDst: pFLDDesc; bCreatable: Boolean): DBIResult; - function AcqTableLock(hCursor: hDBICur; eLockType: word; bNoWait: boolean): DBIResult; - function SetToKey(hCursor: hDBICur;eSearchCond: DBISearchCond;bDirectKey: Boolean;iFields: integer;iLen: integer;pBuff: Pointer): DBIResult; - function CloneCursor(hCurSrc: hDBICur;bReadOnly: Boolean;bUniDirectional: Boolean;var hCurNew: hDBICur): DBIResult; - function SetToCursor(hDest, hSrc : hDBICur) : DBIResult; - function OpenPGNotify(hDb: DAChDBIDb; var hNotify: hDBIObj): DBIResult; - function ClosePGNotify(var hNotify : hDBIObj) : DBIResult; - function ListenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; - function UnlistenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; - function DoNotify(hNotify : hDBIObj; pszEvent: string) : DBIResult; - function DoNotifyEx(hNotify : hDBIObj; pszChannel: string; pszPayload: string) : DBIResult; - function CheckEvents(hNotify : hDBIObj; var Pid : Integer; var pszOutPut, pszPayload : String) : DBIResult; - function GetBackendPID(hDb: DAChDBIDb; var PID: Integer): DBIResult; - function GetServerVersion(hDb: DAChDBIDb; var ServerVersion: string): DBIResult; - function GetUserProps(hDb: DAChDBIDb; const UserName: string; - var SuperUser, CanCreateDB, CanUpdateSysCatalogs: boolean; - var UserID: integer; var ValidUntil: string):DBIResult; - function GetDBProps(hDB: DAChDBIDb; const DB: string; - var Owner, Tablespace: string; - var IsTemplate: boolean; - var DBOid: cardinal; var Comment: string):DBIResult; - function GetTableProps(hDB: DAChDBIDb; const TableName: string; var Owner, - Comment, Tablespace: string; var TableOid: cardinal):DBIResult; - function GetFieldOldValue(hCursor: hDBICur; AFieldName: string; AParam: TParam): DBIResult; - function GetFieldValueFromBuffer(hCursor: hDBICur; PRecord: Pointer; AFieldName: string; AParam: TParam; const UnchangedAsNull: boolean): DBIResult; - function GetLastInsertId(hCursor: hDBICur; const FieldNum: integer; var ID: integer): DBIResult; - function GetFieldTypeOID(hCursor: hDBICur; const FieldNum: integer): cardinal; - function GetFieldOrigin(hCursor: hDBICur; const FieldNum: integer): string; - - function CheckBuffer(hCursor: hDBICur; PRecord: Pointer): DBIResult; - function Reset(hDb: DAChDBIDb): DBIResult; - function CancelBackend(hDb: DAChDBIDb; PID: Integer): DBIResult; - function SelectStringDirect(hDb: DAChDBIDb; - pszQuery : PChar; - var IsOk : boolean; - var aResult : string; - aFieldNumber : integer):DBIResult;overload; - function SelectStringDirect(hDb: DAChDBIDb; - pszQuery : PChar; - var IsOk : boolean; - var aResult : string; - aFieldName : string):DBIResult;overload; - function SelectStringsDirect(hDb: DAChDBIDb; - pszQuery : PChar; - aList : TStrings; - aFieldNumber : integer):DBIResult;overload; - function SelectStringsDirect(hDb: DAChDBIDb; - pszQuery : PChar; - aList : TStrings; - aFieldName : string):DBIResult;overload; - - end; - - ///////////////////////////////////////////////////////// - // Forward declaration // - ///////////////////////////////////////////////////////// - TNativeDataSet = Class; - ////////////////////////////////////////////////////////// - //Class : TPSQLField - //Description : PSQL Field Description - ////////////////////////////////////////////////////////// - - - - TPSQLField = Class(TCollectionItem) - private - FDesc : FldDesc; - FValCheck : VCHKDesc; - FBuffer : Pointer; - FData : Pointer; - FStatus : PFieldStatus; - FArray : Boolean; - FNativeBLOBType: TNativeBLOBType; - function GetLocalSize : integer; - procedure SetLocalSize(S : integer); - function GetLocalType : integer; - procedure SetLocalType(S : integer); - function GetFieldName : String; - procedure SetFieldName(Const Value : String); - procedure SetBuffer(PRecord : Pointer); - function GetChanged : Boolean; - procedure SetChanged(Flag : Boolean); - function GetNull : Boolean; - procedure SetNull(Flag : Boolean); - function GetFieldDefault : string;//mi - procedure SetFieldDefault(aStr : string); - function GetNativeDataset: TNativeDataSet; - function GetNativeConnect: TNativeConnect; - public - constructor CreateField(Owner : TCollection; P : FldDesc; P1 :VCHKDesc; FNum, LType, LSize : integer; isArray : Boolean); - - function FieldValue: PAnsiDACChar; - function FieldValueAsStr: string; //this will be used in SQLs; - - property Buffer : Pointer Read FBuffer Write SetBuffer; - property Data : Pointer Read FData; - property DataOffset : integer Read FDesc.iOffset Write FDesc.iOffset; - property Description : FLDDesc Read FDesc Write FDesc; - property ValCheck : VCHKDesc Read FValCheck Write FValCheck; - property FieldChanged : Boolean Read GetChanged Write SetChanged; - property FieldNull : Boolean Read GetNull Write SetNull; - property FieldStatus : PFieldStatus Read FStatus; - property NullOffset : integer Read FDesc.iNullOffset Write FDesc.iNullOffset; - property FieldNumber : integer Read FDesc.iFldNum Write FDesc.iFldNum; - property FieldName : String Read GetFieldName Write SetFieldName; - property FieldType : integer Read FDesc.iFldType Write FDesc.iFldType; - property FieldSubType : integer Read FDesc.iSubType Write FDesc.iSubType; - property FieldUnits1 : integer Read FDesc.iUnits1 Write FDesc.iUnits1; - property FieldUnits2 : integer Read FDesc.iUnits2 Write FDesc.iUnits2; - property FieldLength : integer Read FDesc.iLen Write FDesc.iLen; - property FieldDefault: string read GetFieldDefault write SetFieldDefault;//mi - property NativeType : integer Read GetLocalType Write SetLocalType; - property NativeSize : integer Read GetLocalSize Write SetLocalSize; - property FieldArray : Boolean Read FArray write FArray; - property NativeBLOBType: TNativeBLOBType read FNativeBLOBType - write FNativeBlobType; - property NativeDataset : TNativeDataSet read GetNativeDataset; - property NativeConnect : TNativeConnect read GetNativeConnect; - end; - - ////////////////////////////////////////////////////////// - //Class : TPSQLFields - //Description : List PSQL Fields for current cursor - ////////////////////////////////////////////////////////// - TPSQLFields = Class(TCollection) - private - FTable : TNativeDataSet; - function GetField(Index : Integer) : TPSQLField; - function GetNativeConnect: TNativeConnect; - public - constructor Create(Table : TNativeDataSet); - function AddField(P : FldDesc; P1 :VCHKDesc; FNum, LType, LSize : integer; isArray : Boolean): TPSQLField; - property Field[Index : Integer] : TPSQLField Read GetField; Default; - procedure SetFields(PRecord : Pointer); - function FieldNumberFromName(SearchName : PChar) : Integer; - - property NativeDataset : TNativeDataSet read FTable; - property NativeConnect : TNativeConnect read GetNativeConnect; - end; - - ////////////////////////////////////////////////////////// - //Class : TPSQLIndex - //Description : PSQL Index Description - ////////////////////////////////////////////////////////// - TPSQLIndex = Class(TCollectionItem) - private - FDesc : IDXDesc; - public - constructor CreateIndex(Owner : TCollection; P : pIDXDesc); - destructor Destroy; override; - property Description : IDXDesc Read FDesc Write FDesc; - property IndexNumber : integer Read FDesc.iIndexID Write FDesc.iIndexID; - property IndexName : string Read FDesc.szName Write FDesc.szName; - property Primary : WordBool Read FDesc.bPrimary Write FDesc.bPrimary; - property Unique : WordBool Read FDesc.bUnique Write FDesc.bUnique; - property Descending : WordBool Read FDesc.bDescending Write FDesc.bDescending; - property FldsInKey : integer Read FDesc.iFldsInKey Write FDesc.iFldsInKey; - property KeyLen : integer Read FDesc.iKeyLen Write FDesc.iKeyLen; - property BlockSize : integer Read FDesc.iBlockSize Write FDesc.iBlockSize; - end; - - ////////////////////////////////////////////////////////// - //Class : TPSQLIndexes - //Description : List PSQL Indexes for current cursor - ////////////////////////////////////////////////////////// - TPSQLIndexes = Class(TCollection) - private - FTable : TNativeDataSet; - FUpdated: boolean; - function GetIndex(Index : Integer) : TPSQLIndex; - function FindByName(Name :String): TPSQLIndex; - procedure SetNeedUpdate(const Value: boolean); - Public - constructor Create(Table : TNativeDataSet); - property mIndex[Index : Integer] : TPSQLIndex Read GetIndex; Default; - function SetIndex(Name,Fields : String;aPrimary,aUnique,aDesc : Boolean): integer; - function FieldNumberFromName(SearchName : PChar) : Integer; - property Updated: boolean read FUpdated write SetNeedUpdate; - end; - - ////////////////////////////////////////////////////////// - //Class : TPSQLFilter - //Description : Filtered object - ////////////////////////////////////////////////////////// - TPSQLFilter = class(TObject) - protected - function PerformCANOp(AOperator : CANOp; AOp1, AOp2 : Variant) : Variant; - function PerformCanConst(ANode : PCANConst; ValuesStart : Pointer; Var FldType : TFldType) : Variant; - function TimeOf(const ADateTime: TDateTime): TDateTime; - private - FDataSet : TNativeDataSet; - FExpression : pCANExpr; - FActive : Boolean; - FExprSize : Word; - FRecBuff : Pointer; - FPfFilter : pfGENFilter; - FClientData : Longint; - function GetNodeStart : Integer; - function GetLiteralPtr(AOffset: Word):Pointer; - function GetNodeByOffset(AOffSet : Integer) : PCanNode; - function UnaryNode(ANode : PCANUnary) : Variant; - function BinaryNode(ANode : PCANBinary) : Variant; - function CompareNode(ANode : PCANCompare) : Variant; - function FieldNode(ANode : pCANField) : Variant; - function GetNodeValue(AOffSet : Integer) : Variant; - function CalcExpression(ANode : PCanNode) : Variant; - function ListOfValues(ANode : pCANListElem): Variant; - function PerformLikeCompare(Const Value, Mask : String; CaseSen : Boolean) : Boolean; - function PerformInCompare(AOp1, AOp2 : Variant) : Boolean; - property NodeStart : Integer Read GetNodeStart; - public - constructor Create(Owner : TNativeDataSet; AClientData : Longint; Exp : pCANExpr; pfFilt : pfGENFilter); - Destructor Destroy; Override; - function GetFilterResult(PRecord : Pointer) : Variant; - property Active : Boolean Read FActive Write FActive; - end; - - ////////////////////////////////////////////////////////// - //Class : TPSQLNative - //Description : PSQL Native Field Description - ////////////////////////////////////////////////////////// - TPSQLNative = Class(TCollectionItem) - private - FDesc : TPGField_Info; - Public - constructor CreateNative(Owner : TCollection; P : PPGField_Info); - property Description : TPGField_Info Read FDesc Write FDesc; - Published - property NativeNumber : Integer Read FDesc.FieldIndex Write FDesc.FieldIndex; - property NativeName : String Read FDesc.FieldName Write FDesc.FieldName; - property NativeType : cardinal Read FDesc.FieldType Write FDesc.FieldType; - property NativeSize : Integer Read FDesc.FieldSize Write FDesc.FieldSize; - property NativeMaxSize : Integer Read FDesc.FieldMaxSize Write FDesc.FieldMaxSize; - property NativeDefault : String Read FDesc.FieldDefault Write FDesc.FieldDefault; - property NativeNotNull : boolean read FDesc.FieldNotNull write FDesc.FieldNotNull; - property NativeTypMod : Integer read FDesc.FieldTypMod write FDesc.FieldTypMod; - end; - - ////////////////////////////////////////////////////////// - //Class : TPSQLNatives - //Description : List PSQL Native Fields for current cursor - ////////////////////////////////////////////////////////// - TPSQLNatives = Class(TCollection) - private - FTable : TNativeDataSet; - function GetNative(Index : Integer) : TPSQLNative; - Public - constructor Create(Table : TNativeDataSet); - property Field_Info[Index : Integer] : TPSQLNative Read GetNative; Default; - procedure SetNative(aIndex : Integer; aName : String; aType, aSize, aMaxSize, aTypMod : Integer); - end; - - ////////////////////////////////////////////////////////// - //Class : TNativeDataSet - //Description : Base class for All Objects - ////////////////////////////////////////////////////////// - TNativeDataSet = Class(TObject) - private - RecNo : LongInt; {Record Nomber} - FOMode : DBIOpenMode; {Open mode} - FStatement : PPGresult; {Handle PSQL Cursor } - FFilters : TContainer; {Filters list} - FFilterActive : Boolean; {is Active filter for Query } - FReFetch : Boolean; {Batch Insert allows} - FFetched : boolean; // if dsoFetchOnDemand shows if all rows are consumed - FFieldDescs : TPSQLFields; - FIndexDescs : TPSQLIndexes; - FNativeDescs : TPSQLNatives; {Native field Description} - FLastOperationTime: cardinal; - FKeyNumber : SmallInt; - FIndexName : string; - FPrimaryKeyNumber: SmallInt; - FPreventRememberBuffer : boolean; //prevent record buffer storing while reading BLOB field data - FGetKeyDesc : Boolean; - FKeyDesc : IDXDesc; - Ranges : Boolean; - FRecSize : Integer; - FConnect : TNativeConnect; - FOpen : Boolean; - FAffectedRows : Integer; - FBookOfs : Integer; - FRecordState : TRecordState; - FLastDir : TDir; - FCurrentBuffer : Pointer; - FInternalBuffer : Pointer; - FIsLocked : Boolean; - FReRead : Boolean; - OrderClause : TStrings; - RangeClause : TStrings; - StandartClause : TStrings; - LimitClause : TStrings; - AutoReExec : Boolean; - FLimit : Integer; - FOffset : Integer; - MasterCursor : Pointer; - FBlobHandle : cardinal; - FlocalBHandle : Integer; - FBlobOpen : Boolean; - FSystemNeed : Boolean; - FFieldMinSizes : array of integer; //to decrease FieldMinSize routine access - FFieldTypType : DACAString; //to store pg_type.typtype - FSortingIndex : array of integer; //filled with SortBy method - FSortingFields : string; //"fieldname" ASC|DESC, ... - FOptions : TPSQLDatasetOptions; - FCustomCompareFunc: TPSQLDatasetSortCompare; - procedure SetInternalBuffer(Buffer : Pointer); - function GetInternalBuffer: Pointer; - function GetCurrentBuffer: Pointer; - procedure SetCurrentBuffer(PRecord : Pointer); - procedure SetBufferAddress(P : Pointer); - procedure SetKeyNumber(newValue: SmallInt); - function FieldOffset(iField: Integer): integer; - function GetBookMarkSize: Integer; - function GetIndexCount: Integer; - procedure SetBufBookmark; - procedure SetRecordNumber(RecNom : Longint); - function GetRecordNumber : Longint; - function GetRecCount: LongInt; - procedure InitFieldDescs; - procedure CheckFilter(PRecord : Pointer); - procedure FirstRecord; virtual; - procedure LastRecord; - procedure NextRecord(); - procedure PrevRecord(); - procedure CurrentRecord(ARecNo : LongInt); - procedure GetWorkRecord(eLock : DBILockType; PRecord : Pointer); - procedure LockRecord(eLock : DBILockType); - function FilteredRecord(PRecord : Pointer) : Boolean; - function FetchRecords(const NumberOfRecs: integer = 1): integer; - procedure UpdateFilterStatus; - function FieldCount : Integer; - procedure InternalSortBy(const Fields: array of Integer; const IsReverseOrder : array of boolean); - function GetRecNo: integer; - procedure InternalReadBuffer; - function GetTableName: string; - procedure SetTableName(Name : string); - function CheckUniqueKey(var KeyNumber : integer): Boolean; - procedure GetKeys(Unique: Boolean;var FieldList: TFieldArray; var FieldCount: Integer); - function GetLOUnlinkSQL(ObjOID: string): string; overload; - function GetDeleteSQL(Table: string; PRecord: Pointer): string; - function GetInsertSQL(Table: string; PRecord: Pointer; ReturnUpdated: boolean = False): string; - function GetUpdateSQL(Table: string; OldRecord, PRecord: Pointer; ReturnUpdated: boolean = False): String; - function FieldVal(FieldNo: Integer; FieldPtr : Pointer):String; - ////////////////////////////////////////////////////////// - // PSQL FIELD PARAMS // - ////////////////////////////////////////////////////////// - function FieldName(FieldNum: Integer): String; - function FieldIndex(FieldName: String): Integer; - function FieldSize(FieldNum: Integer): Integer; - function FieldMaxSize(FieldNum: Integer): Integer; - function FieldMaxSizeInBytes(FieldNum: Integer): Integer; - function FieldMinSize(FieldNum: Integer): Integer; - function FieldType(FieldNum: Integer): cardinal; - function FieldTypMod(FieldNum: Integer): Integer; - function FieldTable(FieldNum: integer): cardinal; - function FieldOrigin(FieldNum: integer): string; - function FieldPosInTable(FieldNum: integer): Integer; - function FieldIsNull(FieldNum: Integer): Boolean; - function Field(FieldNum: Integer): string; - function FieldBuffer(FieldNum: Integer): PAnsiDACChar; -// function FieldByName(FieldName: String): string; - function GetSQLClause: string; - function GetBufferSize : integer; Virtual; - function GetWorkBufferSize : integer; virtual; - procedure GetNativeDesc(FieldNo : Integer; var P : FldDesc; var P1: VCHKDesc; Var LocType, LocSize : Integer; var LocArray: Boolean); - procedure NativeToDelphi(P: TPSQLField; PRecord: Pointer; pDest: Pointer; var bBlank: Boolean); - procedure DelphiToNative(P: TPSQLField; PRecord: Pointer;pSrc: Pointer); - procedure CheckParam(Exp : Boolean;BDECODE : Word); - function GetRecordSize: Integer; - function GetFieldInfo(Index : Integer) : TPGFIELD_INFO; - procedure ReOpenTable; - procedure ClearIndexInfo; - function GetFieldTypType(Index: integer): AnsiDACChar; - private - FTableName: string; - property KeyNumber: SmallInt Read FKeyNumber Write SetKeyNumber; - property RecordCount : LongInt Read GetRecCount; - property Fields : TPSQLFields Read FFieldDescs; - property RecordSize : Integer read GetRecordSize; - property FieldInfo[Index: Integer]:TPGFIELD_INFO Read GetFieldInfo; - property BookMarkSize : Integer Read GetBookMarkSize; - property BufferAddress : Pointer Write SetBufferAddress; - property CurrentBuffer : Pointer Read GetCurrentBuffer Write SetCurrentBuffer; - property InternalBuffer : Pointer Read GetInternalBuffer Write SetInternalBuffer; - property IndexCount : Integer Read GetIndexCount; - //insert, update, delete stuff - function UuidValue(P : Pointer; NeedQuote: boolean = True): string; - function StrValue(P : Pointer; NeedQuote: boolean = True): string; - function MemoValue(P : Pointer; NeedQuote: boolean = True): string; - function BlobValue(P : Pointer; Fld: TPSQLField; NeedEscape: boolean = True): string; overload; - function BlobValue(MS: TStream; isBytea: boolean; NeedEscape: Boolean = True): string; overload; - procedure ReadBlock(var iRecords: Integer; pBuf: Pointer); - public - SQLQuery : String; - ROWID : OID; - isQuery : boolean; - constructor Create(PSQL : TNativeConnect; - //Container : TContainer; - AnOptions: TPSQLDatasetOptions; - AName, IndexName : string; - Index : Word; - Limit, Offset : Integer; - ASystem: Boolean = False); - Destructor Destroy; Override; - procedure CompareBookMarks(pBookMark1, pBookMark2 : Pointer; var CmpBkmkResult : CmpBkmkRslt); - procedure GetBookMark(P : Pointer); - function GetLastInsertID(const KeyNumber: integer):integer; - procedure Execute; - procedure OpenTable; - procedure GetField(FieldNo : Word; PRecord : Pointer; pDest : Pointer; var bBlank : Boolean); - procedure PutField(FieldNo: Word;PRecord : Pointer; PSrc:Pointer); - procedure CloseTable; - procedure GetVchkDesc(iValSeqNo: Word; var pvalDesc: VCHKDesc); - procedure GetCursorProps(var curProps : CURProps); - procedure GetFieldDescs(var pFDesc : TFLDDescList); - procedure GetRecordCount(var iRecCount : integer); virtual; - procedure GetNextRecord(eLock : DBILockType; PRecord : Pointer; pRecProps : pRECProps); Virtual; - procedure SetToRecord(RecNo : LongInt); - procedure SetToBookmark(P : Pointer); virtual; - procedure GetRecord(eLock : DBILockType; PRecord : Pointer; pRecProps : pRECProps); - procedure GetPriorRecord(eLock : DBILockType; PRecord : Pointer; pRecProps : pRECProps); - procedure AddFilter(iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr;pfFilter: pfGENFilter; var hFilter : hDBIFilter); - procedure DropFilter(hFilter: hDBIFilter); - procedure ActivateFilter(hFilter : hDBIFilter); - procedure DeactivateFilter(hFilter : hDBIFilter); - procedure GetProp(iProp: integer;PropValue: Pointer; iMaxLen: integer; var iLen: integer); - procedure SetProp(iProp: integer; PropValue : Longint); - procedure SetToBegin; Virtual; - procedure SetToEnd; - procedure ForceReread; - procedure InitRecord(PRecord : Pointer); - procedure InsertRecord(eLock : DBILockType; PRecord : Pointer); - procedure AppendRecord(PRecord : Pointer); - procedure ModifyRecord(OldRecord,PRecord : Pointer; bFreeLock : Boolean;ARecNo : LongInt); - procedure DeleteRecord(PRecord : Pointer); - //-->blob stuff - procedure OpenBlob(PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode); - procedure FreeBlob(PRecord: Pointer;FieldNo: Word); - procedure CloseBlob(FieldNo: Word); - procedure GetBlobSize(PRecord : Pointer; FieldNo : Word; var iSize : integer); - procedure GetBlob(PRecord : Pointer; FieldNo : Word; iOffSet : Longint; iLen : Longint; pDest : Pointer; var iRead : integer); - procedure PutBlob(PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint; pSrc : Pointer); - procedure TruncateBlob(PRecord : Pointer; FieldNo : Word; iLen : Longint); - procedure FreeBlobStreams(PRecord: Pointer); - //<--blob stuff - procedure QuerySetParams(Params : TParams; SQLText : String); - procedure StoredProcSetParams(Params: TParams); - procedure StoredProcGetParams(Params: TParams); - procedure RelRecordLock(bAll: Boolean); - procedure ExtractKey(PRecord: Pointer;pKeyBuf: Pointer); - procedure GetRecordForKey(bDirectKey: Boolean; iFields: integer; iLen: integer; pKey: Pointer; pRecBuff: Pointer; AStrictConformity: boolean = False); - function findrows(const Fields: array of Integer; const SearchFields:array of String; ACaseSen : Boolean; APartLen : Integer; AStrictConformity: boolean = False):int64; - function SetRowPosition(iFields : Integer; LID : Int64; pRecBuffer : Pointer):Boolean; - procedure GetIndexDesc(iIndexSeqNo : Word; var idxDesc : IDXDesc); - procedure GetIndexDescs(Descs : TIDXDescList); - procedure SetRange(bKeyItself : Boolean; iFields1 : Word; iLen1 : Word; pKey1 : Pointer; - bKey1Incl : Boolean; iFields2 : Word; iLen2 : Word; pKey2 : Pointer; bKey2Incl : Boolean); - procedure ResetRange; - procedure SwitchToIndex(pszIndexName : string; pszTagName : string; iIndexId : Word; bCurrRec : Boolean); - procedure SettoSeqNo(iSeqNo: Longint); - procedure EmptyTable; - procedure AddIndex(var IdxDesc: IDXDesc; pszKeyviolName : string); - procedure DeleteIndex(pszIndexName: string; pszIndexTagName: string; iIndexId: Word); - procedure AcqTableLock(eLockType: word; bNoWait: boolean); - procedure SetToKey(eSearchCond: DBISearchCond; bDirectKey: Boolean;iFields: Word;iLen: Word;pBuff: Pointer); - procedure Clone(bReadOnly: Boolean;bUniDirectional: Boolean;var hCurNew: hDBICur); - procedure SetToCursor(hDest : hDBICur); - - property RecordNumber : LongInt Read GetRecordNumber Write SetRecordNumber; - property RecordState: TRecordState Read FRecordState Write FRecordState; - property TableName : string Read GetTableName Write SetTableName; - - property Options: TPSQLDatasetOptions read FOptions write FOptions; - - property FieldTypTypes[Index: integer]: AnsiDACChar read GetFieldTypType; - - procedure FieldOldValue(AFieldName: string; var AParam: TParam); - procedure FieldValueFromBuffer(PRecord: Pointer; AFieldName: string; var AParam: TParam; const UnchangedAsNull: boolean); - - property IsLocked: boolean read FIsLocked write FIsLocked; - property LastOperationTime: cardinal read FLastOperationTime; - function CheckCanLive: boolean; //pasha_golub 14.07.06 - function HasFieldTimeZone(const FldNum: integer):boolean; - procedure SortBy(FieldNames: string); overload; - procedure SortBY(FieldNames: string; Compare: TPSQLDatasetSortCompare); overload; - function IsSortedLocally: boolean; - property PreventRememberBuffer : boolean read FPreventRememberBuffer write FPreventRememberBuffer; - property Connect: TNativeConnect read FConnect; -end; - - TIndexList = Class(TNativeDataSet) - private - Descs : TIDXDescList; - Items : integer; - Position : integer; - Public - constructor Create(PSQL : TNativeConnect; D : TIDXDescList; TotalCount : integer); - destructor Destroy; Override; - procedure SetToBegin; Override; - procedure GetNextRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); override; - procedure GetIdxDesc(Precord: PIdxDesc); - function GetBufferSize : integer; Override; - function GetWorkBufferSize : integer; Override; - procedure SetToBookmark(P : Pointer); override; - procedure GetRecordCount(Var iRecCount : integer); override; - end; - - TFieldList = Class(TNativeDataSet) - private - Descs : TFLDDescList; - Items : integer; - Position : integer; - public - constructor Create(PSQL : TNativeConnect; D : TFLDDescList; TotalCount : integer); - destructor Destroy; Override; - procedure SetToBegin; Override; - function GetBufferSize : integer; Override; - procedure GetNextRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); override; - procedure GetFLDDesc(PRecord: pFLDDesc); - function GetWorkBufferSize : integer; Override; - procedure SetToBookmark(P : Pointer); override; - procedure GetRecordCount(Var iRecCount : integer); override; - end; - - TNativePGNotify = class - protected - FConnect : TNativeConnect; - FHandle : PPGnotify; - procedure InternalExecute(Sql: string); - public - constructor Create(AConnect: TNativeConnect); - destructor Destroy; override; - procedure ListenTo(Event: string); - procedure UnlistenTo(Event: string); - procedure DoNotify(Event: string); - procedure DoNotifyEx(Channel: string; Payload: string); - function CheckEvents(var PID : Integer; var Payload: string): string; - property Handle: PPGnotify read fHandle; - end; - -function AdjustNativeField(iField : TPSQLField; Src, Dest : Pointer; Var Blank : Boolean) : Word; -function AdjustDelphiField(iField : TPSQLField; Src, Dest : Pointer) : Word; -procedure PSQLException(PSQL : TNativeConnect); -procedure PSQLExceptionMsg(PSQL : TNativeConnect; Const ErrorMsg : String ); - - -function BDETOPSQLStr(Field : TFieldDef): String; -function SQLCreateIdxStr(Index : TPSQLIndex;TableName : String;Flds : TPSQLFields): String; - -function _PQSendQuery(AConnection: TNativeConnect; AQuery: string): integer; -function _PQExecute(AConnection: TNativeConnect; AQuery: string): PPGResult; -function _PQExecuteParams(AConnection: TNativeConnect; AQuery: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; -function _PQexecPrepared(AConnection: TNativeConnect; AStmName: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; - -{$IFDEF M_DEBUG} -function PQExec(Handle: PPGconn; AQuery: PAnsiDACChar): PPGresult; -procedure LogDebugMessage(MsgType, Msg: string); - -var SessionStart: cardinal; -{$ENDIF} - - -{$IFDEF DELPHI_5} -function ifThen(aCondition: boolean; IfTrue: string; IfFalse: string = ''): string; overload; -function ifThen(aCondition: boolean; IfTrue: integer; IfFalse: integer = 0): integer; overload; -{$ENDIF} - -{$IFNDEF DELPHI_15} -{$IFDEF DELPHI_12} -function BcdToStr(const Bcd: TBcd; Format: TFormatSettings): string; -function StrToBcd(const AValue: string; Format: TFormatSettings): TBcd; -{$ENDIF} -{$ENDIF} - -{$IFDEF UNDER_DELPHI_6} -function StrToFloat(const S: string; - const FormatSettings: TFormatSettings): Extended; -function FloatToStr(Value: Extended; - const FormatSettings: TFormatSettings): string; -function FormatDateTime(const Format: string; DateTime: TDateTime; - const FormatSettings: TFormatSettings): string; -procedure DateTimeToString(var Result: string; const Format: string; - DateTime: TDateTime; const FormatSettings: TFormatSettings); -{$ENDIF} - -implementation - -uses PSQLDbTables, PSQLMonitor, - {$IFNDEF DELPHI_5}StrUtils,{$ENDIF} - {$IFDEF MSWINDOWS}Windows,{$ENDIF} - {$IFNDEF FPC}DbConsts,{$ENDIF} - {$IFDEF DELPHI_18}{$IFNDEF NEXTGEN}System.AnsiStrings,{$ENDIF}{$ENDIF} - {$IFDEF NEXTGEN}Character,{$ENDIF} - PSQLExtMask, PSQLFields; - -{**************************************************************************} -{ Utility Objects } -{**************************************************************************} - -{$IFDEF TRIAL} -function PQntuples(Res: PPGresult): Integer; -begin - Result := Min(PSQLTypes.PQntuples(Res), 25); -end; -{$ENDIF} - -{$IFDEF M_DEBUG} -var F: TextFile; - DebugFileOpened: boolean = False; - -procedure LogDebugMessage(MsgType, Msg: string); -begin - if not DebugFileOpened or (Msg = EmptyStr) then Exit; - Msg := StringReplace(Msg, '<','<', [rfReplaceAll]); - Msg := StringReplace(Msg, '>','>', [rfReplaceAll]); - WriteLn(F,'',GetTickCount() - SessionStart,' ms', MsgType, '
',Msg,'
'); -end; - -function PQConnectDB(ConnInfo: PAnsiDACChar): PPGconn; -begin - Result := PSQLTypes.PQConnectDB(ConnInfo); - LogDebugMessage('CONN', String(ConnInfo)); -end; - -function PQExec(Handle: PPGconn; AQuery: PAnsiDACChar): PPGresult; -begin - Result := PSQLTypes.PQexec(Handle,AQuery); - LogDebugMessage('EXEC', String(AQuery)); -end; - -function lo_creat(Handle: PPGconn; mode: Integer): Oid; -begin - Result := PSQLTypes.lo_creat(Handle,mode); - LogDebugMessage('loCr', 'LO OID = '+inttostr(Result)); -end; - -function lo_open(Handle: PPGconn; lobjId: Oid; mode: Integer): Integer; -begin - Result := PSQLTypes.lo_open(Handle,lobjId,mode); - LogDebugMessage('loOp', 'oid = '+inttostr(lobjId)+'; fd = '+inttostr(Result)); -end; - -function lo_close(Handle: PPGconn; fd: Integer): Integer; -begin - Result := PSQLTypes.lo_close(Handle,fd); - LogDebugMessage('loCl', 'fd = '+inttostr(fd)); -end; - -function PQerrorMessage(Handle: PPGconn): PAnsiDACChar; -begin - Result := PSQLTypes.PQerrorMessage(Handle); - LogDebugMessage('ERR ', string(Result)); -end; - -procedure OpenDebugFile; -var Name, Time: string; -begin - SessionStart := GetTickCount(); - DateTimeToString(Time, 'dd.mm.yy_hh.nn.ss', Now(), PSQL_FS); - Name := '_' + Time; - Name := ChangeFileExt(GetModuleName(HInstance), Name + '_log.html'); - AssignFile(F, Name); - {$I-} - if FileExists(Name) then - Append(F) - else - Rewrite(F); - {$I+} - DebugFileOpened := IOResult = 0; - if not DebugFileOpened then Exit; - WriteLn(F,'
',''); - LogDebugMessage('INFO',Format('----- Session started at %s -----', [Time])); -end; - -procedure CloseDebugFile; -begin - if not DebugFileOpened then Exit; - LogDebugMessage('INFO','----- Session closed -----'); - WriteLn(F,'
'); - CloseFile(F); -end; -{$ENDIF} - -{$IFDEF UNDER_DELPHI_6} -const -// 8087 status word masks - mIE = $0001; - mDE = $0002; - mZE = $0004; - mOE = $0008; - mUE = $0010; - mPE = $0020; - mC0 = $0100; - mC1 = $0200; - mC2 = $0400; - mC3 = $4000; -const - // 1E18 as a 64-bit integer - Const1E18Lo = $0A7640000; - Const1E18Hi = $00DE0B6B3; - DCon10: Integer = 10; - -procedure GetLocaleFormatSettings(LCID: Integer; - var FormatSettings: TFormatSettings); -begin - with FormatSettings do - DecimalSeparator := '.'; -end; - -function TextToFloat(Buffer: PChar; var Value; - ValueType: TFloatValue; const FormatSettings: TFormatSettings): Boolean; - -const -// 8087 control word -// Infinity control = 1 Affine -// Rounding Control = 0 Round to nearest or even -// Precision Control = 3 64 bits -// All interrupts masked - CWNear: Word = $133F; - -var - Temp: Integer; - CtrlWord: Word; - DecimalSep: Char; - SaveGOT: Integer; -asm - PUSH EDI - PUSH ESI - PUSH EBX - MOV ESI,EAX - MOV EDI,EDX -{$IFDEF PIC} - PUSH ECX - CALL GetGOT - POP EBX - MOV SaveGOT,EAX -{$ELSE} - MOV SaveGOT,0 - MOV EBX,ECX -{$ENDIF} - MOV EAX,FormatSettings - MOV AL,[EAX].TFormatSettings.DecimalSeparator - MOV DecimalSep,AL - FSTCW CtrlWord - FCLEX -{$IFDEF PIC} - FLDCW [EAX].CWNear -{$ELSE} - FLDCW CWNear -{$ENDIF} - FLDZ - CALL @@SkipBlanks - MOV BH, byte ptr [ESI] - CMP BH,'+' - JE @@1 - CMP BH,'-' - JNE @@2 -@@1: INC ESI -@@2: MOV ECX,ESI - CALL @@GetDigitStr - XOR EDX,EDX - MOV AL,[ESI] - CMP AL,DecimalSep - JNE @@3 - INC ESI - CALL @@GetDigitStr - NEG EDX -@@3: CMP ECX,ESI - JE @@9 - MOV AL, byte ptr [ESI] - AND AL,0DFH - CMP AL,'E' - JNE @@4 - INC ESI - PUSH EDX - CALL @@GetExponent - POP EAX - ADD EDX,EAX -@@4: CALL @@SkipBlanks - CMP BYTE PTR [ESI],0 - JNE @@9 - MOV EAX,EDX - CMP BL,fvCurrency - JNE @@5 - ADD EAX,4 -@@5: PUSH EBX - MOV EBX,SaveGOT - CALL FPower10 - POP EBX - CMP BH,'-' - JNE @@6 - FCHS -@@6: CMP BL,fvExtended - JE @@7 - FISTP QWORD PTR [EDI] - JMP @@8 -@@7: FSTP TBYTE PTR [EDI] -@@8: FSTSW AX - TEST AX,mIE+mOE - JNE @@10 - MOV AL,1 - JMP @@11 -@@9: FSTP ST(0) -@@10: XOR EAX,EAX -@@11: FCLEX - FLDCW CtrlWord - FWAIT - JMP @@Exit - -@@SkipBlanks: - -@@21: LODSB - OR AL,AL - JE @@22 - CMP AL,' ' - JE @@21 -@@22: DEC ESI - RET - -// Process string of digits -// Out EDX = Digit count - -@@GetDigitStr: - - XOR EAX,EAX - XOR EDX,EDX -@@31: LODSB - SUB AL,'0'+10 - ADD AL,10 - JNC @@32 -{$IFDEF PIC} - XCHG SaveGOT,EBX - FIMUL [EBX].DCon10 - XCHG SaveGOT,EBX -{$ELSE} - FIMUL DCon10 -{$ENDIF} - MOV Temp,EAX - FIADD Temp - INC EDX - JMP @@31 -@@32: DEC ESI - RET - -// Get exponent -// Out EDX = Exponent (-4999..4999) - -@@GetExponent: - - XOR EAX,EAX - XOR EDX,EDX - MOV CL, byte ptr [ESI] - CMP CL,'+' - JE @@41 - CMP CL,'-' - JNE @@42 -@@41: INC ESI -@@42: MOV AL, byte ptr [ESI] - SUB AL,'0'+10 - ADD AL,10 - JNC @@43 - INC ESI - IMUL EDX,10 - ADD EDX,EAX - CMP EDX,500 - JB @@42 -@@43: CMP CL,'-' - JNE @@44 - NEG EDX -@@44: RET - -@@Exit: - POP EBX - POP ESI - POP EDI -end; - -procedure PutExponent; -// Store exponent -// In AL = Exponent character ('E' or 'e') -// AH = Positive sign character ('+' or 0) -// BL = Zero indicator -// ECX = Minimum number of digits (0..4) -// EDX = Exponent -// EDI = Destination buffer -asm - PUSH ESI -{$IFDEF PIC} - PUSH EAX - PUSH ECX - CALL GetGOT - MOV ESI,EAX - POP ECX - POP EAX -{$ELSE} - XOR ESI,ESI -{$ENDIF} - STOSB - OR BL,BL - JNE @@0 - XOR EDX,EDX - JMP @@1 -@@0: OR EDX,EDX - JGE @@1 - MOV AL,'-' - NEG EDX - JMP @@2 -@@1: OR AH,AH - JE @@3 - MOV AL,AH -@@2: STOSB -@@3: XCHG EAX,EDX - PUSH EAX - MOV EBX,ESP -@@4: XOR EDX,EDX - DIV [ESI].DCon10 - ADD DL,'0' - MOV [EBX],DL - INC EBX - DEC ECX - OR EAX,EAX - JNE @@4 - OR ECX,ECX - JG @@4 -@@5: DEC EBX - MOV AL,[EBX] - STOSB - CMP EBX,ESP - JNE @@5 - POP EAX - POP ESI -end; - - -function FloatToText(BufferArg: PChar; const Value; ValueType: TFloatValue; - Format: TFloatFormat; Precision, Digits: Integer; - const FormatSettings: TFormatSettings): Integer; -var - Buffer: Cardinal; - FloatRec: TFloatRec; - SaveGOT: Integer; - DecimalSep: Char; - ThousandSep: Char; - CurrencyStr: Pointer; - CurrFmt: Byte; - NegCurrFmt: Byte; -asm - PUSH EDI - PUSH ESI - PUSH EBX - MOV Buffer,EAX -{$IFDEF PIC} - PUSH ECX - CALL GetGOT - MOV SaveGOT,EAX - POP ECX -{$ENDIF} - MOV EAX,FormatSettings - MOV AL,[EAX].TFormatSettings.DecimalSeparator - MOV DecimalSep,AL - MOV EAX,FormatSettings - MOV AL,[EAX].TFormatSettings.ThousandSeparator - MOV ThousandSep,AL - MOV EAX,FormatSettings - MOV EAX,[EAX].TFormatSettings.CurrencyString - MOV CurrencyStr,EAX - MOV EAX,FormatSettings - MOV AL,[EAX].TFormatSettings.CurrencyFormat - MOV CurrFmt,AL - MOV EAX,FormatSettings - MOV AL,[EAX].TFormatSettings.NegCurrFormat - MOV NegCurrFmt,AL - MOV SaveGOT,0 - MOV EAX,19 - CMP CL,fvExtended - JNE @@2 - MOV EAX,Precision - CMP EAX,2 - JGE @@1 - MOV EAX,2 -@@1: CMP EAX,18 - JLE @@2 - MOV EAX,18 -@@2: MOV Precision,EAX - PUSH EAX - MOV EAX,9999 - CMP Format,ffFixed - JB @@3 - MOV EAX,Digits -@@3: PUSH EAX - LEA EAX,FloatRec - CALL FloatToDecimal - MOV EDI,Buffer - MOVZX EAX,FloatRec.Exponent - SUB EAX,7FFFH - CMP EAX,2 - JAE @@4 - MOV ECX, EAX - CALL @@PutSign - LEA ESI,@@INFNAN[ECX+ECX*2] - ADD ESI,SaveGOT - MOV ECX,3 - REP MOVSB - JMP @@7 -@@4: LEA ESI,FloatRec.Digits - MOVZX EBX,Format - CMP BL,ffExponent - JE @@6 - CMP BL,ffCurrency - JA @@5 - MOVSX EAX,FloatRec.Exponent - CMP EAX,Precision - JLE @@6 -@@5: MOV BL,ffGeneral -@@6: LEA EBX,@@FormatVector[EBX*4] - ADD EBX,SaveGOT - MOV EBX,[EBX] - ADD EBX,SaveGOT - CALL EBX -@@7: MOV EAX,EDI - SUB EAX,Buffer - POP EBX - POP ESI - POP EDI - JMP @@Exit - -@@FormatVector: - DD @@PutFGeneral - DD @@PutFExponent - DD @@PutFFixed - DD @@PutFNumber - DD @@PutFCurrency - -@@INFNAN: DB 'INFNAN' - -// Get digit or '0' if at end of digit string - -@@GetDigit: - - LODSB - OR AL,AL - JNE @@a1 - MOV AL,'0' - DEC ESI -@@a1: RET - -// Store '-' if number is negative - -@@PutSign: - - CMP FloatRec.Negative,0 - JE @@b1 - MOV AL,'-' - STOSB -@@b1: RET - -// Convert number using ffGeneral format - -@@PutFGeneral: - - CALL @@PutSign - MOVSX ECX,FloatRec.Exponent - XOR EDX,EDX - CMP ECX,Precision - JG @@c1 - CMP ECX,-3 - JL @@c1 - OR ECX,ECX - JG @@c2 - MOV AL,'0' - STOSB - CMP BYTE PTR [ESI],0 - JE @@c6 - MOV AL,DecimalSep - STOSB - NEG ECX - MOV AL,'0' - REP STOSB - JMP @@c3 -@@c1: MOV ECX,1 - INC EDX -@@c2: LODSB - OR AL,AL - JE @@c4 - STOSB - LOOP @@c2 - LODSB - OR AL,AL - JE @@c5 - MOV AH,AL - MOV AL,DecimalSep - STOSW -@@c3: LODSB - OR AL,AL - JE @@c5 - STOSB - JMP @@c3 -@@c4: MOV AL,'0' - REP STOSB -@@c5: OR EDX,EDX - JE @@c6 - XOR EAX,EAX - JMP @@PutFloatExpWithDigits -@@c6: RET - -// Convert number using ffExponent format - -@@PutFExponent: - - CALL @@PutSign - CALL @@GetDigit - MOV AH,DecimalSep - STOSW - MOV ECX,Precision - DEC ECX -@@d1: CALL @@GetDigit - STOSB - LOOP @@d1 - MOV AH,'+' - -@@PutFloatExpWithDigits: - - MOV ECX,Digits - CMP ECX,4 - JBE @@PutFloatExp - XOR ECX,ECX - -// Store exponent -// In AH = Positive sign character ('+' or 0) -// ECX = Minimum number of digits (0..4) - -@@PutFloatExp: - - MOV AL,'E' - MOV BL, FloatRec.Digits.Byte - MOVSX EDX,FloatRec.Exponent - DEC EDX - CALL PutExponent - RET - -// Convert number using ffFixed or ffNumber format - -@@PutFFixed: -@@PutFNumber: - - CALL @@PutSign - -// Store number in fixed point format - -@@PutNumber: - - MOV EDX,Digits - CMP EDX,18 - JB @@f1 - MOV EDX,18 -@@f1: MOVSX ECX,FloatRec.Exponent - OR ECX,ECX - JG @@f2 - MOV AL,'0' - STOSB - JMP @@f4 -@@f2: XOR EBX,EBX - CMP Format,ffFixed - JE @@f3 - MOV EAX,ECX - DEC EAX - MOV BL,3 - DIV BL - MOV BL,AH - INC EBX -@@f3: CALL @@GetDigit - STOSB - DEC ECX - JE @@f4 - DEC EBX - JNE @@f3 - MOV AL,ThousandSep - TEST AL,AL - JZ @@f3 - STOSB - MOV BL,3 - JMP @@f3 -@@f4: OR EDX,EDX - JE @@f7 - MOV AL,DecimalSep - TEST AL,AL - JZ @@f4b - STOSB -@@f4b: JECXZ @@f6 - MOV AL,'0' -@@f5: STOSB - DEC EDX - JE @@f7 - INC ECX - JNE @@f5 -@@f6: CALL @@GetDigit - STOSB - DEC EDX - JNE @@f6 -@@f7: RET - -// Convert number using ffCurrency format - -@@PutFCurrency: - - XOR EBX,EBX - MOV BL,CurrFmt.Byte - MOV ECX,0003H - CMP FloatRec.Negative,0 - JE @@g1 - MOV BL,NegCurrFmt.Byte - MOV ECX,040FH -@@g1: CMP BL,CL - JBE @@g2 - MOV BL,CL -@@g2: ADD BL,CH - LEA EBX,@@MoneyFormats[EBX+EBX*4] - ADD EBX,SaveGOT - MOV ECX,5 -@@g10: MOV AL,[EBX] - CMP AL,'@' - JE @@g14 - PUSH ECX - PUSH EBX - CMP AL,'$' - JE @@g11 - CMP AL,'*' - JE @@g12 - STOSB - JMP @@g13 -@@g11: CALL @@PutCurSym - JMP @@g13 -@@g12: CALL @@PutNumber -@@g13: POP EBX - POP ECX - INC EBX - LOOP @@g10 -@@g14: RET - -// Store currency symbol string - -@@PutCurSym: - - PUSH ESI - MOV ESI,CurrencyStr - TEST ESI,ESI - JE @@h1 - MOV ECX,[ESI-4] - REP MOVSB -@@h1: POP ESI - RET - -// Currency formatting templates - -@@MoneyFormats: - DB '$*@@@' - DB '*$@@@' - DB '$ *@@' - DB '* $@@' - DB '($*)@' - DB '-$*@@' - DB '$-*@@' - DB '$*-@@' - DB '(*$)@' - DB '-*$@@' - DB '*-$@@' - DB '*$-@@' - DB '-* $@' - DB '-$ *@' - DB '* $-@' - DB '$ *-@' - DB '$ -*@' - DB '*- $@' - DB '($ *)' - DB '(* $)' - -@@Exit: -end; - - -resourcestring - SInvalidFloat = '''%s'' is not a valid floating point value'; - -function StrToFloat(const S: string; - const FormatSettings: TFormatSettings): Extended; -begin - if not TextToFloat(PChar(S), Result, fvExtended, FormatSettings) then - raise EConvertError.CreateResFmt(@SInvalidFloat, [S]); -end; - -function FloatToStr(Value: Extended; - const FormatSettings: TFormatSettings): string; -var - Buffer: array[0..63] of Char; -begin - SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended, - ffGeneral, 15, 0, FormatSettings)); -end; - -function FormatDateTime(const Format: string; DateTime: TDateTime; - const FormatSettings: TFormatSettings): string; -begin - DateTimeToString(Result, Format, DateTime, FormatSettings); -end; - -procedure DateTimeToString(var Result: string; const Format: string; - DateTime: TDateTime; const FormatSettings: TFormatSettings); -var - BufPos, AppendLevel: Integer; - Buffer: array[0..255] of Char; - - procedure AppendChars(P: PChar; Count: Integer); - var - N: Integer; - begin - N := SizeOf(Buffer) - BufPos; - if N > Count then N := Count; - if N <> 0 then Move(P[0], Buffer[BufPos], N); - Inc(BufPos, N); - end; - - procedure AppendString(const S: string); - begin - AppendChars(Pointer(S), Length(S)); - end; - - procedure AppendNumber(Number, Digits: Integer); - const - Format: array[0..3] of Char = '%.*d'; - var - NumBuf: array[0..15] of Char; - begin - AppendChars(NumBuf, FormatBuf(NumBuf, SizeOf(NumBuf), Format, - SizeOf(Format), [Digits, Number])); - end; - - procedure AppendFormat(Format: PChar); - var - Starter, Token, LastToken: Char; - DateDecoded, TimeDecoded, Use12HourClock, - BetweenQuotes: Boolean; - P: PChar; - Count: Integer; - Year, Month, Day, Hour, Min, Sec, MSec, H: Word; - - procedure GetCount; - var - P: PChar; - begin - P := Format; - while Format^ = Starter do Inc(Format); - Count := Format - P + 1; - end; - - procedure GetDate; - begin - if not DateDecoded then - begin - DecodeDate(DateTime, Year, Month, Day); - DateDecoded := True; - end; - end; - - procedure GetTime; - begin - if not TimeDecoded then - begin - DecodeTime(DateTime, Hour, Min, Sec, MSec); - TimeDecoded := True; - end; - end; - - function ConvertEraString(const Count: Integer) : string; - var - FormatStr: string; - SystemTime: TSystemTime; - Buffer: array[Byte] of Char; - P: PChar; - begin - Result := ''; - with SystemTime do - begin - wYear := Year; - wMonth := Month; - wDay := Day; - end; - - FormatStr := 'gg'; - if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime, - PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then - begin - Result := Buffer; - if Count = 1 then - begin - case SysLocale.PriLangID of - LANG_JAPANESE: - Result := Copy(Result, 1, CharToBytelen(Result, 1)); - LANG_CHINESE: - if (SysLocale.SubLangID = SUBLANG_CHINESE_TRADITIONAL) - and (ByteToCharLen(Result, Length(Result)) = 4) then - begin - P := Buffer + CharToByteIndex(Result, 3) - 1; - SetString(Result, P, CharToByteLen(P, 2)); - end; - end; - end; - end; - end; - - function ConvertYearString(const Count: Integer): string; - var - FormatStr: string; - SystemTime: TSystemTime; - Buffer: array[Byte] of Char; - begin - Result := ''; - with SystemTime do - begin - wYear := Year; - wMonth := Month; - wDay := Day; - end; - - if Count <= 2 then - FormatStr := 'yy' // avoid Win95 bug. - else - FormatStr := 'yyyy'; - - if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime, - PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then - begin - Result := Buffer; - if (Count = 1) and (Result[{$IFNDEF NEXTGEN}1{$ELSE}0{$ENDIF}] = '0') then - Result := Copy(Result, 2, Length(Result)-1); - end; - end; - - function StrCharLength(const Str: PChar): Integer; - begin - if SysLocale.FarEast then - Result := Integer(CharNext(Str)) - Integer(Str) - else - Result := 1; - end; - - function StrNextChar(const Str: PChar): PChar; - begin - Result := CharNext(Str); - end; - - begin - if (Format <> nil) and (AppendLevel < 2) then - begin - Inc(AppendLevel); - LastToken := ' '; - DateDecoded := False; - TimeDecoded := False; - Use12HourClock := False; - while Format^ <> #0 do - begin - Starter := Format^; - if Starter in LeadBytes then - begin - AppendChars(Format, StrCharLength(Format)); - Format := StrNextChar(Format); - LastToken := ' '; - Continue; - end; - Format := StrNextChar(Format); - Token := Starter; - if Token in ['a'..'z'] then Dec(Token, 32); - if Token in ['A'..'Z'] then - begin - if (Token = 'M') and (LastToken = 'H') then Token := 'N'; - LastToken := Token; - end; - case Token of - 'Y': - begin - GetCount; - GetDate; - if Count <= 2 then - AppendNumber(Year mod 100, 2) else - AppendNumber(Year, 4); - end; - 'G': - begin - GetCount; - GetDate; - AppendString(ConvertEraString(Count)); - end; - 'E': - begin - GetCount; - GetDate; - AppendString(ConvertYearString(Count)); - end; - 'M': - begin - GetCount; - GetDate; - case Count of - 1, 2: AppendNumber(Month, Count); - 3: AppendString(FormatSettings.ShortMonthNames[Month]); - else - AppendString(FormatSettings.LongMonthNames[Month]); - end; - end; - 'D': - begin - GetCount; - case Count of - 1, 2: - begin - GetDate; - AppendNumber(Day, Count); - end; - 3: AppendString(FormatSettings.ShortDayNames[DayOfWeek(DateTime)]); - 4: AppendString(FormatSettings.LongDayNames[DayOfWeek(DateTime)]); - 5: AppendFormat(Pointer(FormatSettings.ShortDateFormat)); - else - AppendFormat(Pointer(FormatSettings.LongDateFormat)); - end; - end; - 'H': - begin - GetCount; - GetTime; - BetweenQuotes := False; - P := Format; - while P^ <> #0 do - begin - if P^ in LeadBytes then - begin - P := StrNextChar(P); - Continue; - end; - case P^ of - 'A', 'a': - if not BetweenQuotes then - begin - if ( (StrLIComp(P, 'AM/PM', 5) = 0) - or (StrLIComp(P, 'A/P', 3) = 0) - or (StrLIComp(P, 'AMPM', 4) = 0) ) then - Use12HourClock := True; - Break; - end; - 'H', 'h': - Break; - '''', '"': BetweenQuotes := not BetweenQuotes; - end; - Inc(P); - end; - H := Hour; - if Use12HourClock then - if H = 0 then H := 12 else if H > 12 then Dec(H, 12); - if Count > 2 then Count := 2; - AppendNumber(H, Count); - end; - 'N': - begin - GetCount; - GetTime; - if Count > 2 then Count := 2; - AppendNumber(Min, Count); - end; - 'S': - begin - GetCount; - GetTime; - if Count > 2 then Count := 2; - AppendNumber(Sec, Count); - end; - 'T': - begin - GetCount; - if Count = 1 then - AppendFormat(Pointer(FormatSettings.ShortTimeFormat)) else - AppendFormat(Pointer(FormatSettings.LongTimeFormat)); - end; - 'Z': - begin - GetCount; - GetTime; - if Count > 3 then Count := 3; - AppendNumber(MSec, Count); - end; - 'A': - begin - GetTime; - P := Format - 1; - if StrLIComp(P, 'AM/PM', 5) = 0 then - begin - if Hour >= 12 then Inc(P, 3); - AppendChars(P, 2); - Inc(Format, 4); - Use12HourClock := TRUE; - end else - if StrLIComp(P, 'A/P', 3) = 0 then - begin - if Hour >= 12 then Inc(P, 2); - AppendChars(P, 1); - Inc(Format, 2); - Use12HourClock := TRUE; - end else - if StrLIComp(P, 'AMPM', 4) = 0 then - begin - if Hour < 12 then - AppendString(FormatSettings.TimeAMString) else - AppendString(FormatSettings.TimePMString); - Inc(Format, 3); - Use12HourClock := TRUE; - end else - if StrLIComp(P, 'AAAA', 4) = 0 then - begin - GetDate; - AppendString(FormatSettings.LongDayNames[DayOfWeek(DateTime)]); - Inc(Format, 3); - end else - if StrLIComp(P, 'AAA', 3) = 0 then - begin - GetDate; - AppendString(FormatSettings.ShortDayNames[DayOfWeek(DateTime)]); - Inc(Format, 2); - end else - AppendChars(@Starter, 1); - end; - 'C': - begin - GetCount; - AppendFormat(Pointer(FormatSettings.ShortDateFormat)); - GetTime; - if (Hour <> 0) or (Min <> 0) or (Sec <> 0) then - begin - AppendChars(' ', 1); - AppendFormat(Pointer(FormatSettings.LongTimeFormat)); - end; - end; - '/': - if DateSeparator <> #0 then - AppendChars(@FormatSettings.DateSeparator, 1); - ':': - if TimeSeparator <> #0 then - AppendChars(@FormatSettings.TimeSeparator, 1); - '''', '"': - begin - P := Format; - while (Format^ <> #0) and (Format^ <> Starter) do - begin - if Format^ in LeadBytes then - Format := StrNextChar(Format) - else - Inc(Format); - end; - AppendChars(P, Format - P); - if Format^ <> #0 then Inc(Format); - end; - else - AppendChars(@Starter, 1); - end; - end; - Dec(AppendLevel); - end; - end; - -begin - BufPos := 0; - AppendLevel := 0; - if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C'); - SetString(Result, Buffer, BufPos); -end; -{$ENDIF} - -{$IFNDEF DELPHI_15} -{$IFDEF DELPHI_12} -function TryStrToBcd(const AValue: string; var Bcd: TBcd; Format: TFormatSettings): Boolean; - - function IsSpaceChar(theChar: Char): Boolean; - begin - Result := False; - if (theChar = ' ') or (theChar = #6) or (theChar = #10) or (theChar = #13) or (theChar = #14) then - Result := True; - end; - -var - Negative: Boolean; - PStr: PChar; - DecimalPos, Exp: Integer; - Pos: Byte; -begin - FillChar(Bcd, SizeOf(Bcd), #0); - PStr := PChar(AValue); - while IsSpaceChar(PStr^) do - Inc(PStr); - Negative := PStr^ = '-'; - if (Pstr^ = '-') or (PStr^ = '+') then - Inc(PStr); - // Skip leading 0s; - while PStr^ = '0' do - Inc(PStr); - - Pos := 0; - DecimalPos := -1; - while PStr^ <> #0 do - begin - if (PStr^ = Format.DecimalSeparator) then - begin - if DecimalPos <> -1 then Exit(False); - if Pos = 0 then - Inc(Pos); - DecimalPos := Pos; - Inc(PStr); - if (PStr^ = #0) then - Break; - end; - if IsSpaceChar(PStr^) or (PStr^ = 'E') or (PStr^ = 'e') then - Break; - - if (PStr^ < '0') or (PStr^ > '9') then - Exit(False); - - if (Pos = 64) and (DecimalPos = -1) then - Exit(False); // Too many digits - if Pos < 64 then - begin - if (Pos and 1) = 0 then - Bcd.Fraction[Pos div 2] := Byte(Ord(PStr^) - Ord('0')) * $10 - else - Bcd.Fraction[Pos div 2] := (Bcd.Fraction[Pos div 2] and $F0) + Byte(Ord(PStr^) - Ord('0')); - Inc(Pos); - end; - Inc(PStr); - end; - - // Scientific notation - if (PStr^ = 'E') or (PStr^ = 'e') then // Most typical situation: X.XXEYYY. DecimalPos = 1 - begin - if not TryStrToInt(PChar(@PStr[1]), Exp) then - Exit(False); - if DecimalPos < 0 then - begin - DecimalPos := Pos; - Inc(Pos); - end; - if Exp < 0 then - begin - begin - if DecimalPos < -Exp then - begin - bcd.Precision := Pos; - bcd.SignSpecialPlaces := Pos -1; - Exp := Pos - Exp; - Pos := Pos - DecimalPos; - if Exp > MaxFMTBcdFractionSize then - begin - dec(Pos, Exp - MaxFMTBcdFractionSize); - DecimalPos := Exp - MaxFMTBcdFractionSize; - Exp := MaxFMTBcdFractionSize; - end; - if not NormalizeBcd(bcd, bcd, Exp, Pos) then - Exit(False); - Pos := Exp; - end - else - Inc(DecimalPos, Exp); - - end; - end - else - begin - inc(DecimalPos, Exp); - if DecimalPos > Pos then - begin - Pos := DecimalPos; - DecimalPos := -1; - end; - end; - - end else - begin - while IsSpaceChar(PStr^) do - Inc(PStr); - if PStr^ <> #0 then - Exit(False); - end; - - if Pos = 0 then - begin - Bcd.Precision := 10; - Bcd.SignSpecialPlaces := 2; - end - else - begin - if Pos > 64 then - Exit(False); - Bcd.Precision := Pos; - if DecimalPos = -1 then - Bcd.SignSpecialPlaces := 0 - else - Bcd.SignSpecialPlaces := Pos - DecimalPos; - // Because it's easier to shift bytes than nibbles, - // Always make it an even precision, add a 0 if needed - if (Pos and 1) = 1 then - begin - Inc(Bcd.Precision); - Inc(Bcd.SignSpecialPlaces); - end; - - if Negative then - Bcd.SignSpecialPlaces := Bcd.SignSpecialPlaces or $80; - end; - Result := True; -end; - -procedure BcdErrorFmt(const Message, BcdAsString: string); -begin - raise EBcdException.CreateFmt(Message, [BcdAsString]); -end; - -procedure OverflowError(const Message: string); -begin - raise EBcdOverflowException.Create(Message); -end; - -function StrToBcd(const AValue: string; Format: TFormatSettings): TBcd; -begin - if not TryStrToBcd(AValue, Result, Format) then - BcdErrorFmt(SInvalidBcdValue, AValue); -end; - -procedure PutChar(var Buf: PChar; C: Char); -begin - Buf^ := C; - Inc(Buf); -end; - -function BcdToStr(const Bcd: TBcd; Format: TFormatSettings): string; -var - Buf: array [0..66] of Char; //64 Nibbles + 1 sign + 1 decimal + #0 - PBuf: PChar; - DecimalPos: Byte; - I: Integer; -begin - if Bcd.Precision = 0 then - Exit('0'); - if (Bcd.Precision > MaxFMTBcdFractionSize) or - ((Bcd.SignSpecialPlaces and $3F) > Bcd.Precision) then - OverflowError(SBcdOverflow); - PBuf := @Buf[1]; - DecimalPos := Bcd.Precision - Bcd.SignSpecialPlaces and $3F; - for I := 0 to Bcd.Precision - 1 do - begin - if I = DecimalPos then - begin - if I = 0 then - PutChar(PBuf, '0'); - PutChar(PBuf, Format.DecimalSeparator); - end; - if (I and 1) = 0 then - PutChar(PBuf, Char( ((Bcd.Fraction[I div 2] and $F0) SHR 4) + ord('0')) ) - else - PutChar(PBuf, Char( ((Bcd.Fraction[I div 2] and $0F)) + ord('0')) ); - end; - // Remove trailing 0s after decmial - Dec(PBuf); - I := Bcd.Precision; - while (I > DecimalPos) and (PBuf^ = '0') do - begin - Dec(PBuf); - Dec(I); - end; - if PBuf^ = Format.DecimalSeparator then - PBuf^ := #0 - else - PBuf[1] := #0; - PBuf := @Buf[1]; - // Remove leading 0s before decimal - while PBuf^ = '0' do - Inc(PBuf); - if (PBuf^ = #0) or (PBuf^ = Format.DecimalSeparator) then - Dec(PBuf); - - if ((Bcd.SignSpecialPlaces and $80) = $80) and - not ((PBuf^ = '0') and (PBuf[1] = #0)) then // only add - if not 0 - begin - Dec(PBuf); - PBuf^ := '-'; - end; - Result := PChar(PBuf); -end; -{$ENDIF} -{$ENDIF} - -function _PQSendQuery(AConnection: TNativeConnect; AQuery: string): integer; -var Q: PAnsiDACChar; - S: DACAString; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin -{$IFDEF M_DEBUG} - LogDebugMessage('SEND', String(AQuery)); -{$ENDIF} - if AConnection.IsUnicodeUsed then - S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AQuery)) - else - S := DACAString(AQuery); - GetMem(Q, Length(S) + 1); - try - {$IFNDEF NEXTGEN} - DACStrCopy(Q, PAnsiChar(S)); - {$ELSE} - DACStrCopy(Q, M.AsAnsi(S).ToPointer); - {$ENDIF} - Result := PQsendQuery(AConnection.Handle, Q); - finally - FreeMem(Q); - end; -end; - -function _PQExecute(AConnection: TNativeConnect; AQuery: string): PPGResult; -var Q: PAnsiDACChar; - S: DACAString; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if AConnection.IsUnicodeUsed then - S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AQuery)) - else - S := DACAString(AQuery); - GetMem(Q, Length(S) + 1); - try - {$IFNDEF NEXTGEN} - DACStrCopy(Q, PAnsiChar(S)); - {$ELSE} - DACStrCopy(Q, M.AsAnsi(S).ToPointer); - {$ENDIF} - Result := PQExec(AConnection.Handle, Q); - finally - FreeMem(Q); - end; -end; - -function _PQexecPrepared(AConnection: TNativeConnect; AStmName: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; -var Q: PAnsiDACChar; - S: DACAString; - paramValues: array of PAnsiDACChar; - i: integer; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if AConnection.IsUnicodeUsed then - S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AStmName)) - else - S := DACAString(AStmName); - GetMem(Q, Length(S) + 1); - try - {$IFNDEF NEXTGEN} - DACStrCopy(Q, PAnsiChar(S)); - {$ELSE} - DACStrCopy(Q, M.AsAnsi(S).ToPointer); - {$ENDIF} - SetLength(paramValues, AParams.Count); - for i := 0 to AParams.Count - 1 do - begin - if AConnection.IsUnicodeUsed then - S := {$IFDEF NEXTGEN}string{$ENDIF}(UTF8Encode(AParams[i].AsString)) - else - S := DACAString(AParams[i].AsString); - GetMem(paramValues[i], Length(S) + 1); - {$IFNDEF NEXTGEN} - DACStrCopy(paramValues[i], PAnsiChar(S)); - {$ELSE} - DACStrCopy(paramValues[i], M.AsAnsi(S).ToPointer); - {$ENDIF} - end; - try - Result := PQexecPrepared(AConnection.Handle, Q, AParams.Count, @paramValues[0], nil, nil, AResultFormat); - finally - for i := 0 to AParams.Count - 1 do - FreeMem(paramValues[i]); - end; - finally - FreeMem(Q); - end; -end; - -function _PQExecuteParams(AConnection: TNativeConnect; AQuery: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; -var Q: PAnsiDACChar; - S: DACAString; - paramValues: array of PAnsiDACChar; - i: integer; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if AConnection.IsUnicodeUsed then - S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AQuery)) - else - S := DACAString(AQuery); - GetMem(Q, Length(S) + 1); - try - {$IFNDEF NEXTGEN} - DACStrCopy(Q, PAnsiChar(S)); - {$ELSE} - DACStrCopy(Q, M.AsAnsi(S).ToPointer); - {$ENDIF} - SetLength(paramValues, AParams.Count); - for i := 0 to AParams.Count - 1 do - begin - if AParams[i].IsNull then - begin - paramValues[i] := nil; - Continue; - end; - if AConnection.IsUnicodeUsed then - S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AParams[i].AsString)) - else - S := DACAString(AParams[i].AsString); - GetMem(paramValues[i], Length(S) + 1); - {$IFNDEF NEXTGEN} - DACStrCopy(paramValues[i], PAnsiChar(S)); - {$ELSE} - DACStrCopy(paramValues[i], M.AsAnsi(S).ToPointer); - {$ENDIF} - end; - try - Result := PQexecParams(AConnection.Handle, Q, AParams.Count, nil, @paramValues[0], nil, nil, AResultFormat); - finally - for i := 0 to AParams.Count - 1 do - FreeMem(paramValues[i]); - end; - finally - FreeMem(Q); - end; -end; - -function _PQConnectDBParams(AParams: TStrings; ExpandDbName: boolean = False): PPGConn; -var - ConnKeywords, ConnValues: packed array of PAnsiDACChar; - K,V: DACAString; - i: integer; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - SetLength(ConnKeywords, AParams.Count + 1); - SetLength(ConnValues, AParams.Count + 1); - for i := 0 to AParams.Count - 1 do - begin - K := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AParams.Names[i])); //since this is connection assume we'll use UTF8 - GetMem(ConnKeywords[i], Length(K) + 1); - {$IFNDEF NEXTGEN} - DACStrCopy(ConnKeywords[i], PAnsiChar(K)); - {$ELSE} - DACStrCopy(ConnKeywords[i], M.AsAnsi(K).ToPointer); - {$ENDIF} - {$IFDEF DELPHI_7} - V := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AParams.ValueFromIndex[i])); - {$ELSE} - V := UTF8Encode(Copy(AParams[I], Length(K) + 2, MaxInt)); - {$ENDIF} - GetMem(ConnValues[i], Length(V) + 1); - {$IFNDEF NEXTGEN} - DACStrCopy(ConnValues[i], PAnsiChar(V)); - {$ELSE} - DACStrCopy(ConnValues[i], M.AsAnsi(V).ToPointer); - {$ENDIF} - end; - try - {$IFDEF M_DEBUG} - LogDebugMessage('CONN', AParams.CommaText); - {$ENDIF} - ConnKeywords[High(ConnKeywords)] := nil; - ConnValues[High(ConnValues)] := nil; - - Result := PQconnectdbParams(@ConnKeywords[0], @ConnValues[0], ord(ExpandDbName)); - finally - for i := 0 to AParams.Count - 1 do - begin - FreeMem(ConnValues[i]); - FreeMem(ConnKeywords[i]); - end; - end; -end; - -function TimeOf(const ADateTime: TDateTime): TDateTime; -var - Hour, Min, Sec, MSec: Word; -begin - DecodeTime(ADateTime, Hour, Min, Sec, MSec); - Result := EncodeTime(Hour, Min, Sec, MSec); -end; - -function AdjustNativeField(iField :TPSQLField; Src,Dest: Pointer; Var Blank : Boolean): Word; -begin - Result := 0; - Blank := PAnsiDACChar(Src)^ = #0; - if Blank then Exit; - Inc(PAnsiDACChar(Src)); - case iField.NativeType of - FIELD_TYPE_BOOL: SmallInt(Dest^) := SmallInt(Src^); - - FIELD_TYPE_INT2: SmallInt(Dest^) := SmallInt(Src^); - FIELD_TYPE_INT4: LongInt(Dest^) := LongInt(Src^); - FIELD_TYPE_INT8: Int64(Dest^) := Int64(Src^); - - FIELD_TYPE_DATE: LongInt(Dest^) := DateTimeToTimeStamp(TDateTime(Src^)).Date; - FIELD_TYPE_TIME: LongInt(Dest^) := DateTimeToTimeStamp(TDateTime(Src^)).Time; - FIELD_TYPE_TIMESTAMP: TDateTime(Dest^):= TimeStampToMSecs(DateTimeToTimeStamp(TDateTime(Src^))); - - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: Double(Dest^) := Double(Src^); - - FIELD_TYPE_NUMERIC: TBcd(Dest^) := TBcd(Src^); - -{$IFDEF DELPHI_12} - FIELD_TYPE_POINT: TPSQLPoint(Dest^) := TPSQLPoint(Src^); - FIELD_TYPE_CIRCLE: TPSQLCircle(Dest^) := TPSQLCircle(Src^); - FIELD_TYPE_BOX: TPSQLBox(Dest^) := TPSQLBox(Src^); - FIELD_TYPE_LSEG: TPSQLLSeg(Dest^) := TPSQLLSeg(Src^); - - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE : TPSQLRange(Dest^) := TPSQLRange(Src^); -{$ENDIF DELPHI_12} - - FIELD_TYPE_BYTEA, - FIELD_TYPE_OID, - FIELD_TYPE_TEXT: Result := 1; - else - StrLCopy(PChar(Dest), PChar(Src), iField.FieldLength - 1); //minus null byte - end; - Blank := Result <> 0; -end; - -function AdjustDelphiField(iField: TPSQLField; Src, Dest: Pointer): Word; -var - TimeStamp: TTimeStamp; -begin - {$IFNDEF NEXTGEN} - ZeroMemory(Dest, iField.NativeSize); - {$ELSE} - FillChar(Dest^, iField.NativeSize, 0); - {$ENDIF} - PAnsiDACChar(Dest)^ := #1; - Inc(PAnsiDACChar(Dest), 1); - Result:=0; - - case iField.NativeType of - FIELD_TYPE_BOOL: SmallInt(Dest^) := SmallInt(Src^); - FIELD_TYPE_INT2: SmallInt(Dest^) := SmallInt(Src^); - FIELD_TYPE_INT4: LongInt(Dest^) := LongInt(Src^); - FIELD_TYPE_INT8: Int64(Dest^) := Int64(Src^); - FIELD_TYPE_DATE: begin - try - TimeStamp.Date := LongInt(Src^); - TimeStamp.Time := 0; - TDateTime(Dest^) := TimeStampToDateTime(TimeStamp); - except - Result := 1; - end; - end; - FIELD_TYPE_TIME: begin - try - TimeStamp.Date := DateDelta; - TimeStamp.Time := LongInt(Src^); - TDateTime(Dest^) := TimeStampToDateTime(TimeStamp); - except - Result := 1; - end; - end; - - FIELD_TYPE_TIMESTAMP: begin - try - TDateTime(Dest^):= TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ELSE}Double{$ENDIF}(Src^))); - except - Result:=1; - end; - end; - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: Double(Dest^) := Double(Src^); - FIELD_TYPE_NUMERIC: TBcd(Dest^) := TBcd(Src^); - -{$IFDEF DELPHI_12} - FIELD_TYPE_POINT: TPSQLPoint(Dest^) := TPSQLPoint(Src^); - FIELD_TYPE_CIRCLE: TPSQLCircle(Dest^) := TPSQLCircle(Src^); - FIELD_TYPE_BOX: TPSQLBox(Dest^) := TPSQLBox(Src^); - FIELD_TYPE_LSEG: TPSQLLSeg(Dest^) := TPSQLLSeg(Src^); - - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: TPSQLRange(Dest^) := TPSQLRange(Src^); -{$ENDIF DELPHI_12} - - FIELD_TYPE_OID, - FIELD_TYPE_BYTEA, - FIELD_TYPE_TEXT: Result := 1; - else - {$IFDEF DELPHI_12} - if iField.NativeDataset.FConnect.IsUnicodeUsed then - {$IFNDEF NEXTGEN} - CopyMemory(Dest, Src, iField.NativeSize) - {$ELSE} - Move(Src^, Dest^, iField.NativeSize) - {$ENDIF} - else - {$ENDIF} - DACStrCopy(PAnsiDACChar(Dest), PAnsiDACChar(Src), iField.NativeSize); - end; - - if Result = 1 then - begin - {$IFNDEF NEXTGEN} - ZeroMemory(Dest, iField.NativeSize); - {$ELSE} - FillChar(Dest^, iField.NativeSize, 0); - {$ENDIF} - Result := 0; - end; -end; - -procedure PSQLException(PSQL : TNativeConnect); -begin - raise EPSQLException.Create(PSQL); -end; - -procedure PSQLExceptionMsg(PSQL : TNativeConnect; Const ErrorMsg : String ); -begin - raise EPSQLException.CreateMsg(PSQL, ErrorMsg ); -end; - -function BDETOPSQLStr(Field : TFieldDef): String; -const - _IsVarChar: array[boolean] of string = ('CHAR','VARCHAR'); - _IntNames: array[boolean,boolean] of string = (('INT4','SERIAL'),('INT8','BIGSERIAL')); -var - isAutoInc: Boolean; - isInt8: boolean; - - ColName: string; -begin - Result :=''; - ColName := AnsiQuotedStr(Field.Name,'"'); - case Field.DataType of - ftString, - ftFixedChar : begin - Result := Format('%s %s',[ColName,_IsVarChar[(Field.DataType = ftFixedChar) or (faFixed in Field.Attributes)]]); - if Field.Size > 0 then - Result := Result + Format('(%s)',[IntToStr(Field.Size)]); - end; - - ftDate : Result := Format('%s DATE',[ColName]); - ftBlob, - ftBytes, - ftVarBytes : Result := Format('%s BYTEA',[ColName]); - ftMemo : Result := Format('%s TEXT',[ColName]); - ftBoolean : Result := Format('%s BOOL',[ColName]); - ftSmallint, - ftWord - {$IFDEF DELPHI_15} - ,ftShortInt - {$ENDIF} - : Result := Format('%s INT2',[ColName]); - - ftInteger, - {$IFDEF DELPHI_15} - ftLongWord, - {$ENDIF} - ftLargeint, - ftAutoInc : begin - isAutoInc := ftAutoInc = Field.DataType; - isInt8 := Field.DataType = ftLargeint; - Result := Format('%s %s',[ColName,_IntNames[isInt8,isAutoInc]]); - end; - - ftFloat, - ftBCD : Result := Format('%s NUMERIC(%s,%s)',[ColName,IntToStr(Field.Size),IntToStr(Field.Precision)]); - - ftTime : Result := Format('%s TIME',[ColName]); - - {$IFDEF DELPHI_15} - ftTimeStamp, - {$ENDIF} - ftDateTime: Result := Format('%s DATETIME',[ColName]); - end; - if Field.Required then - Result := Result + ' NOT NULL'; -end; - -function SQLCreateIdxStr(Index : TPSQLIndex;TableName : String;Flds : TPSQLFields): String; - - function GetFieldList:String; - var - I : Integer; - S : String; - begin - S :=''; - for I :=0 to Index.FldsInKey-1 do - begin - S := S+ AnsiQuotedStr(Flds.Field[Index.Description.aiKeyFld[I]].FieldName,'"'); - if I < Index.FldsInKey-1 then S := S+','; - end; - Result := S; - end; - -var IdxName: string; - Tbl: PChar; - -begin - result := ''; - - if Index.IndexName = '' then - begin - Tbl := Pchar(TableName); - IdxName := AnsiExtractQuotedStr(Tbl,'"'); - if Index.Primary then - idxName := 'PK_'+IdxName - else - if Index.Unique then - idxName := 'UNI_'+IdxName - else - idxName := 'IDX_'+IdxName - end - else - IdxName := Index.IndexName; - IdxName := AnsiQuotedStr(IdxName,'"'); - - if Index.Primary then - Result := Format('ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY(%s);',[TableName,IdxName,GetFieldList]) - else - if Index.Unique then - Result := Format('CREATE UNIQUE INDEX %s ON %s (%s);',[IdxName,TableName,GetFieldList]) - else - Result := Format('CREATE INDEX %s ON %s (%s);',[IdxName,TableName,GetFieldList]); -end; - -{******************************************************************************} -{ EPSQLError *} -{******************************************************************************} -constructor EPSQLException.CreateBDE(ECode : Word); -begin - Inherited Create(''); - FBDEErrorCode := ECode; - FBDE := True; -end; - -constructor EPSQLException.CreateBDEMsg(ECode : Word; Const EMessage : string); -begin - inherited; - FPSQLErrorMsg := string(EMessage); - CreateBDE(ECode); -end; - -constructor EPSQLException.Create(PSQL : TNativeConnect); -begin - FPSQL := PSQL; - FPSQLErrorCode := 1; - FPSQLErrorMsg := PSQL.GetErrorText; - if FPSQLErrorCode > 0 then FBDEERRORCode := DBIERR_INVALIDPARAM; - Inherited Create(''); -end; - -constructor EPSQLException.CreateMsg(PSQL : TNativeConnect; const ErrorMsg : String ); -begin - Create(PSQL); - FPSQLErrorMsg := ErrorMsg; - FBDEERRORCode := 1001; -end; - -{******************************************************************************} -{ TNativeConnect *} -{******************************************************************************} -constructor TNativeConnect.Create; -begin - Inherited Create; - FLoggin := False; - FHandle := nil; - FGUCList := TStringList.Create; -end; - -Destructor TNativeConnect.Destroy; -begin - InternalDisconnect; - FreeAndNil(FGUCList); - Inherited Destroy; -end; - -procedure TNativeConnect.DirectExecute(SQL: string); -var - LocHandle: PPGconn; - locResult: PPGresult; - ErrStr: String; - OldLoggin : Boolean; -begin - if SQLLibraryHandle <= HINSTANCE_ERROR then LoadPSQLLibrary(); - OldLoggin := FLoggin; - if FLoggin then InternalDisconnect; - with DBOptions do - LocHandle := PQconnectdb(PAnsiDACChar(UTF8Encode((FDirectConnectString)))); - if not Assigned(LocHandle) then Exit; - LocResult := _PQExecute(Self, SQL); - if Assigned(LocResult) then - begin - ErrStr := RawToString(PQerrorMessage(LocHandle)); - PQclear(LocResult); - end; - PQfinish(LocHandle); - if OldLoggin then InternalConnect; - if ErrStr <> '' then - raise EPSQLException.CreateMsg(self,ErrStr); -end; - -{$IFDEF DELPHI_5} -function ifThen(aCondition: boolean; IfTrue, IfFalse: string): string; -begin - if aCondition then Result := IfTrue else Result := IfFalse; -end; - -function ifThen(aCondition: boolean; IfTrue: integer; IfFalse: integer = 0): integer; -begin - if aCondition then Result := IfTrue else Result := IfFalse; -end; -{$ENDIF} - -class function TNativeConnect.Ping(Params: TStrings): TPingStatus; -var ConnStr: string; - i: integer; -begin - ConnStr := ''; - for i := 0 to Params.Count - 1 do - ConnStr := ConnStr + Params[i] + ' '; - Result := PQping(PAnsiDACChar(Utf8Encode(ConnStr))); -end; - -procedure TNativeConnect.ProcessDBParams(Params : TStrings); -var i: integer; -begin - for i := 0 to Params.Count - 1 do - begin - FConnectString := FConnectString + Params[i] + ' '; - if Params.Names[i] = 'dbname' then - FDirectConnectString := FDirectConnectString + 'dbname=template1 ' - else - FDirectConnectString := Params[i] + ' '; - end; -end; - -procedure TNativeConnect.InternalConnect(ConnParams: TStrings = nil); -var - Result: PPGresult; - Utf8Encoded: PAnsiDACChar; -begin - if not FLoggin then - try - FHandle := nil; - if SQLLibraryHandle <= HINSTANCE_ERROR then - LoadPSQLLibrary(); - FLastOperationTime := GetTickCount; - if Assigned(ConnParams) then - FHandle := _PQConnectDBParams(ConnParams) - else - begin - Utf8Encoded := PAnsiDACChar(UTF8Encode(FConnectString)); - FHandle := PQConnectDB(Utf8Encoded); - end; - FLastOperationTime := GetTickCount - FLastOperationTime; - if PQstatus(FHandle) = CONNECTION_BAD then - CheckResult(); - Result := PQExec(FHandle, 'SET DateStyle TO ''ISO, MDY'''); - PQclear(Result); - FLoggin := True; - ReloadGUC(); - MonitorHook.DBConnect(Self, True); - except - MonitorHook.DBConnect(Self, False); - if Assigned(FHandle) then - PQfinish(FHandle); - raise; - end; -end; - -function TNativeConnect.GetBackendPID: Integer; -begin - Result := PQbackendPID(Handle); -end; - -procedure TNativeConnect.InternalDisconnect; -begin - if FLoggin then - begin - FLastOperationTime := GetTickCount; - PQfinish(Handle); - FLastOperationTime := GetTickCount - FLastOperationTime; - Handle := nil; - FLoggin := False; - FServerVersion := ''; - FGUCList.Clear; - MonitorHook.DBDisconnect(Self); - end; -end; - -function TNativeConnect.GetErrorText: String; -begin - Result := RawToString(PQerrorMessage(Handle)); -end; - -function TNativeConnect.GetNativeByteaFormat: TNativeByteaFormat; -begin - if FGUCList.Values['bytea_output'] = 'hex' then - Result := nbfHex - else - Result := nbfEscape; -end; - -function TNativeConnect.Success: Boolean; -begin - Result := GetErrorText = ''; -end; - -function TNativeConnect.RollBack: boolean; -var - Res: PPGresult; -begin - Res := PQexec(Handle, 'ROLLBACK'); - Result := PQresultStatus(Res) = PGRES_COMMAND_OK; - PQclear(Res); -end; - -function TNativeConnect.Commit: boolean; -var - Res: PPGresult; -begin - Res := PQexec(Handle, 'COMMIT'); - Result := PQresultStatus(Res) = PGRES_COMMAND_OK; - PQclear(Res); -end; - - -procedure TNativeConnect.CheckResult; -var S: string; -begin - S := GetErrorText(); - if S > '' then - begin - FErrorSeverity := ''; - FErrorSQLState := ''; - FErrorPrimary := ''; - FErrorDetail := ''; - FErrorHint := ''; - FErrorInternalPos := ''; - FErrorInternalQuery := ''; - FErrorSourceFile := ''; - FErrorSourceLine := ''; - FErrorSourceFunc := ''; - FErrorContext := ''; - FErrorPos := ''; - FErrorSchemaName := ''; - FErrorTableName := ''; - FErrorColumnName := ''; - FErrorDataTypeName := ''; - FErrorConstraintName := ''; - raise EPSQLException.CreateMsg(Self, S); - end; -end; - -procedure TNativeConnect.CheckResult(FStatement: PPGresult); -var - S: string; -begin - S := GetErrorText(); - if S > '' then - begin - FErrorSeverity := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SEVERITY))); - FErrorSQLState := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SQLSTATE))); - FErrorPrimary := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_MESSAGE_PRIMARY))); - FErrorDetail := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_MESSAGE_DETAIL))); - FErrorHint := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_MESSAGE_HINT))); - FErrorInternalPos := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_INTERNAL_POSITION))); - FErrorInternalQuery := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_INTERNAL_QUERY))); - FErrorSourceFile := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SOURCE_FILE))); - FErrorSourceLine := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SOURCE_LINE))); - FErrorSourceFunc := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SOURCE_FUNCTION))); - FErrorContext := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_CONTEXT))); - FErrorPos := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_STATEMENT_POSITION))); - FErrorSchemaName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SCHEMA_NAME))); - FErrorTableName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_TABLE_NAME))); - FErrorColumnName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_COLUMN_NAME))); - FErrorDataTypeName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_DATATYPE_NAME))); - FErrorConstraintName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_CONSTRAINT_NAME))); - raise EPSQLException.CreateMsg(Self, S); - end; -end; - -procedure TNativeConnect.TableList(pszWild: string; SystemTables: Boolean; List: TStrings); -var - I : LongInt; - sql : String; - RES : PPGresult; -begin - InternalConnect; - List.Clear; - - Sql := 'SELECT c.oid :: regclass FROM pg_class as c, pg_namespace as ns' + - ' WHERE c.relkind IN (''r'', ''v'') AND (ns.oid = c.relnamespace)'; - - if not SystemTables then - Sql := SQL + ' AND (ns.nspname NOT LIKE ''pg_%'')'+ - ' AND (ns.nspname NOT LIKE ''information_schema'')'; - - if pszWild > '' then - Sql := Sql + ' AND relname LIKE '''+ pszWild+ ''''; - Sql := Sql + ' ORDER BY 1'; - RES := _PQExecute(Self, Sql); - try - CheckResult; - if Assigned(RES) then - for I := 0 to PQntuples(RES) - 1 do - List.Add(RawToString(PQgetvalue(RES,I,0))); - finally - PQclear(RES); - end; -end; - -procedure TNativeConnect.UserList(pszWild : string; List : TStrings); -var - I : LongInt; - sql : String; - RES : PPGresult; -begin - InternalConnect; - Sql := 'SELECT usename FROM pg_shadow '; - if pszWild <> '' then - Sql := Sql + ' WHERE usename LIKE ''' + pszWild + ''''; - Sql := Sql + ' ORDER BY 1'; - RES := _PQExecute(Self, Sql); - try - if Assigned(RES) then - begin - CheckResult; - for I := 0 to PQntuples(RES)-1 do - begin - List.Add(RawToString(PQgetvalue(RES,I,0))); - end; - end; - finally - PQclear(RES); - end; -end; - -procedure TNativeConnect.SchemaList(pszWild : string; SystemSchemas: Boolean; List : TStrings); -var - I : LongInt; - sql : String; - RES : PPGresult; -begin - InternalConnect; - Sql := 'SELECT nspname FROM pg_namespace WHERE True '; - if pszWild <> '' then - Sql := Sql + ' AND nspname LIKE ''' + pszWild + ''''; - if not SystemSchemas then - begin - Sql := Sql + ' AND nspname NOT IN (''pg_catalog'', ''pg_toast'', '+ - '''pg_sysviews'', ''information_schema'') '; - if GetserverVersionAsInt > 080200 then - Sql := Sql + 'AND nspname NOT LIKE E''pg\\_temp\\_%'' AND nspname NOT LIKE E''pg\\_toast_temp\\_%''' - else - Sql := Sql + 'AND nspname NOT LIKE ''pg\\_temp\\_%'' AND nspname NOT LIKE ''pg\\_toast_temp\\_%''' - end; - Sql := Sql + ' ORDER BY 1'; - RES := _PQExecute(Self, Sql); - try - if Assigned(RES) then - begin - CheckResult; - for I := 0 to PQntuples(RES)-1 do - begin - List.Add(RawToString(PQgetvalue(RES,I,0))); - end; - end; - finally - PQclear(RES); - end; -end; - -procedure TNativeConnect.DatabaseList(pszWild : string; List :TStrings); -var - CRec : string; - I : LongInt; - sql : String; - RES : PPGresult; -begin - InternalConnect; - List.Clear; - Sql := 'SELECT datname FROM pg_database'; - if pszWild <> '' then - Sql := Sql + ' WHERE datname LIKE '''+pszWild+''''; - Sql := Sql + ' ORDER BY datname'; - RES := _PQExecute(Self, Sql); - if Assigned(RES) then - begin - for I := 0 to PQntuples(RES)-1 do - begin - CREC := RawToString(PQgetvalue(RES,I,0)); - List.Add(CREC); - end; - end; - PQclear(RES); -end; - -procedure TNativeConnect.OpenTable(pszTableName: string; - pszIndexName: string; - iIndexId: Word; - eOpenMode: DBIOpenMode; - eShareMode: DBIShareMode; - var hCursor: hDBICur; - AnOptions: TPSQLDatasetOptions; - Limit, Offset : integer); -begin - InternalConnect; - if FSystem then - begin - hCursor := hDBICur(TNativeDataSet.Create(Self, AnOptions, pszTableName, pszIndexName, iIndexId,1,0,True)); - FSystem := False; - end else - hCursor := hDBICur(TNativeDataSet.Create(Self, AnOptions, pszTableName, pszIndexName, iIndexId,Limit,Offset)); - TNativeDataSet(hCursor).OpenTable; -end; - -procedure TNativeConnect.QueryAlloc(var hStmt: hDBIStmt); -begin - hStmt := hDBIStmt(TNativeDataSet.Create(Self, [], '' , '', 0, 0, 0)); -end; - -procedure TNativeConnect.QueryPrepare(var hStmt: hDBIStmt;Query : String); -begin - FLastOperationTime := GetTickCount; - TNativeDataSet(hStmt).SQLQuery := Query; - TNativeDataSet(hStmt).isQuery := True; - FLastOperationTime := GetTickCount - FLastOperationTime; - MonitorHook.SQLPrepare(TNativeDataSet(hStmt)); -end; - -procedure TNativeConnect.BeginTran(eXIL: eXILType; var hXact: hDBIXact); -var - Res: PPGresult; - Status: ExecStatusType; - TransParam: DACAString; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if FTransState = xsActive then Exit; - hXact := hDBIXact(Self); - TransParam := 'START TRANSACTION ISOLATION LEVEL '; - case eXIL of - xilDIRTYREAD, - xilREADCOMMITTED: TransParam := TransParam + 'READ COMMITTED'; - xilREPEATABLEREAD: TransParam := TransParam + 'REPEATABLE READ'; - xilSERIALIZABLE: TransParam := TransParam + 'SERIALIZABLE'; - end; - Res := PQExec(Handle, {$IFNDEF NEXTGEN}PAnsiChar(TransParam){$ELSE}M.AsAnsi(TransParam).ToPointer{$ENDIF}); - try - Status := PQresultStatus(Res); - MonitorHook.TRStart(Self, Status = PGRES_COMMAND_OK); - if Status = PGRES_COMMAND_OK then - begin - FTransState := xsActive; - FTransLevel := eXIL; - end - else - CheckResult; - finally - PQclear(Res); - end -end; - -procedure TNativeConnect.EndTran(hXact : hDBIXact; eEnd : eXEnd); -begin - if eEnd = xendABORT then - MonitorHook.TRRollback(Self, Rollback) - else - MonitorHook.TRCommit(Self, Commit); - CheckResult; - FTransState := xsInactive; -end; - -procedure TNativeConnect.GetTranInfo(hXact : hDBIXact; pxInfo : pXInfo); -begin - {$IFNDEF NEXTGEN} - ZeroMemory(pxInfo, Sizeof(pxInfo^)); - {$ELSE} - FillChar(pxInfo^, Sizeof(pxInfo^), 0); - {$ENDIF} - if GetTransactionStatus in [trstACTIVE, trstINTRANS, trstINERROR] then - FTransState := xsActive; - pxInfo^.eXState := FTransState; - pxInfo^.eXIL := FTransLevel; -end; - -procedure TNativeConnect.QExecDirect(pszQuery : String; phCur: phDBICur; var AffectedRows : integer); -var - hStmt : hDBIStmt; -begin - hStmt := nil; - QueryAlloc(hStmt); - QueryPrepare(hStmt, pszQuery); - if hStmt <> nil then - try - FLastOperationTime := GetTickCount; - TNativeDataSet(hStmt).Execute; - finally - FLastOperationTime := GetTickCount - FLastOperationTime; - AffectedRows := TNativeDataSet(hStmt).FAffectedRows; - TNativeDataSet(hStmt).Free; - end; -end; - -procedure TNativeConnect.OpenFieldList(pszTableName: string; - pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur); -var - P: TNativeDataSet; - Props: curProps; - Items: Integer; - Descs: TFLDDescList; -begin - FSystem := True; - OpenTable(pszTableName, '', 0, dbiREADONLY, dbiOPENSHARED, hDBICur(P), - [], 0, 0); - P.GetCursorProps(Props); - Items := Props.iFields; - if Items > 0 then - begin - SetLength(Descs, Items); - try - P.GetFieldDescs(Descs); - hCur := hDBICur(TFieldList.Create(Self, Descs, Items)); - finally - Finalize(Descs); - end; - end; - P.CloseTable; - P.Free; - FSystem := False; -end; - -procedure TNativeConnect.OpenIndexList(pszTableName: string; pszDriverType: string; var hCur: hDBICur); -var - P : hDBICur; - Ind : TIndexList; - - procedure ProcessTable; - var - Props : CURProps; - Items : Word; - Descs : TIDXDescList; - begin - Descs := nil; - TNativeDataSet(P).GetCursorProps(Props); - Items := Props.iIndexes; - try - if Items > 0 then - begin - SetLength(Descs, Items); - TNativeDataSet(P).GetIndexDescs(Descs); - end else - Descs := nil; - Ind := TIndexList.Create(Self, Descs, Items); - hCur := hDBICur(Ind); - finally - Finalize(Descs); - end; - end; - - procedure OpenAndProcessTable; - begin - FSystem := True; - OpenTable(pszTableName, '', 0, dbiREADONLY, dbiOPENSHARED, P, [], 0, 0); - try - ProcessTable; - TNativeDataSet(P).CloseTable; - finally - TNativeDataSet(P).Free; - end; - FSystem := False; - end; - -begin - hCur := nil; - try - OpenAndProcessTable; - except - On E:EPSQLException do OpenAndProcessTable; - end; -end; - -function TNativeConnect.GetCharSet: string; -begin - Result := 'UNDEFINED'; - if not FLoggin then Exit; - Result := UpperCase(string(PQparameterStatus(Handle, 'client_encoding'))); - FCharset := Result; - FUnicodeUsed := (FCharset = 'UNICODE') or (FCharset = 'UTF8'); -end; - -procedure TNativeConnect.EmptyTable(hCursor : hDBICur; pszTableName : string); -var - isNotOpen : Boolean; -begin - isNotOpen := not Assigned(hCursor); - if isNotOpen then - OpenTable(pszTableName, '', 0, dbiREADWRITE, dbiOPENEXCL, hCursor, [], 0, 0); - try - TNativeDataSet(hCursor).EmptyTable; - finally - if isNotOpen then - TNativeDataSet(hCursor).Free; - end; -end; - -procedure TNativeConnect.AddIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; var IdxDesc: IDXDesc; pszKeyviolName: string); -var - NDS : TNativeDataSet; -begin - if Assigned(hCursor) then - NDS := TNativeDataSet(hCursor) else - OpenTable(pszTableName, '', IdxDesc.iIndexId, dbiREADWRITE, dbiOPENEXCL, hDBICur(NDS), [], 0, 0); - try - NDS.AddIndex(idxDesc,pszKeyViolName); - finally - if not Assigned(hCursor) then NDS.Free; - end; -end; - -procedure TNativeConnect.DeleteIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; pszIndexName: string; pszIndexTagName: string; iIndexId: Word); -var - NDS : TNativeDataSet; -begin - if Assigned(hCursor) then - NDS := TNativeDataSet(hCursor) else - OpenTable(pszTableName, pszIndexName, iIndexId, dbiREADWRITE, dbiOPENEXCL, hDBICur(NDS), [], 0, 0); - try - NDS.DeleteIndex(pszIndexName, pszIndexTagName, iIndexID); - finally - if not Assigned(hCursor) then NDS.Free; - end; -end; - - -////////////////////////////////////////////////////////// -//constructor : TPSQLField.CreateField -//Description : constructor CreateNewField -////////////////////////////////////////////////////////// -//Input : Owner: TCollection -// P: pFldDesc -// FNum: Word -// LType: Word -// LSize: Word -////////////////////////////////////////////////////////// -constructor TPSQLField.CreateField(Owner : TCollection; P : FldDesc; P1 : VCHKDesc; FNum, LType, LSize : integer; isArray : Boolean); -begin - Create(Owner); - - FDesc := P; - FValCheck := P1; - - FieldNumber := FNum; - NativeType := LType; - Case NativeType of - FIELD_TYPE_BYTEA: NativeBLOBType := nbtBytea; - FIELD_TYPE_OID: NativeBLOBType := nbtOID - else - NativeBLOBType := nbtNotBlob; - end; - NativeSize := LSize; //Max(LSize, FDesc.iLen); - - FieldArray := isArray; -end; - -function TPSQLField.GetFieldName : String; -begin - Result := string(FDesc.szName); -end; - -procedure TPSQLField.SetFieldName(Const Value : String); -begin - StrPCopy(@FDesc.szName, Copy(Value,1,SizeOf(FDesc.szName)-1)); -end; - -procedure TPSQLField.SetBuffer(PRecord : Pointer); -begin - FBuffer := PRecord; - if FBuffer <> nil then - begin - FData := FBuffer; - Inc(PAnsiDACChar(FData), FDesc.iOffset); - if FDesc.INullOffset > 0 then - begin - FStatus := FBuffer; - Inc(PAnsiDACChar(FStatus), FDesc.iNullOffset); - end else - FStatus := NIL; - end else - begin - FData := nil; - FStatus := nil; - end; -end; - -function TPSQLField.GetNativeDataset: TNativeDataSet; -begin - Result := TPSQLFields(Collection).NativeDataset; -end; - -function TPSQLField.GetNativeConnect: TNativeConnect; -begin - Result := TPSQLFields(Collection).NativeConnect; -end; - -function TPSQLField.GetNull : Boolean; -var AVal: PAnsiDACChar; -begin - Result := True; - case NativeType of - FIELD_TYPE_BPCHAR, - FIELD_TYPE_VARCHAR: if (dsoEmptyCharAsNull in GetNativeDataset.Options) then - begin - AVal := FieldValue; - Inc(AVal); - if AVal = '' then Exit; //we have empty string and it's treated as NULL due options - end; - end; - if FStatus <> nil then - Result := TFieldStatus(FStatus^).isNULL = -1 - else - Result := FALSE; -end; - -procedure TPSQLField.SetNull( Flag : Boolean ); -Const - VALUES : Array[ Boolean ] of SmallInt = ( 0, -1 ); -begin - if FStatus <> nil then FStatus^.isNULL := VALUES[ Flag ]; -end; - -function TPSQLField.GetChanged : Boolean; -begin - if FStatus <> nil then Result := TFieldStatus(FStatus^).Changed else Result := TRUE; -end; - -procedure TPSQLField.SetChanged(Flag : Boolean); -begin - if FStatus <> nil then TFieldStatus(FStatus^).Changed := Flag; -end; - -function TPSQLField.GetLocalSize : integer; -begin - Result := FDesc.iUnused[1]; -end; - -procedure TPSQLField.SetLocalSize(S : integer); -begin - FDesc.iUnused[1] := S; -end; - -function TPSQLField.GetLocalType : integer; -begin - Result := FDesc.iUnused[0]; -end; - -procedure TPSQLField.SetLocalType(S : integer); -begin - FDesc.iUnused[0] := S; -end; - -function TPSQLField.FieldValue: PAnsiDACChar; -begin - Result := PAnsiDACChar(PAnsiDACChar(FData) + FieldNumber - 1); -end; - -function TPSQLField.FieldValueAsStr: string; - var Src: pointer; - - function SimpleQuote(const S: string): string; - begin - Result := '''' + S + ''''; - end; - -begin - Src := FieldValue(); - Inc(PAnsiDACChar(Src)); - case FieldType of - fldBOOL: Result := IfThen(SmallInt(Src^) > 0, 'TRUE', 'FALSE'); - fldINT16: Result := IntToStr(SmallInt(Src^)); - fldINT32: Result := IntToStr(LongInt(Src^)); - fldINT64: Result := IntToStr(Int64(Src^)); - fldFloat: Result := SQLFloatToStr(Double(Src^)); -{$IFDEF DELPHI_12} - fldFMTBCD: Result := BcdToStr(TBcd(Src^), PSQL_FS); -{$ENDIF} - fldZSTRING: - case NativeType of - FIELD_TYPE_BIT, - FIELD_TYPE_VARBIT: Result := 'B' + NativeDataset.StrValue(Src) - else - Result := NativeDataset.StrValue(Src); - end; - fldUUID: Result := NativeDataset.UuidValue(Src); - fldBLOB: if FieldSubType = fldstMemo then - Result := NativeDataset.MemoValue(Src) - else - Result := NativeDataset.BlobValue(Src, Self); - fldDate: Result := SimpleQuote(DateTimeToSqlDate(TDateTime(Src^), DATE_MODE)); - fldTime: Result := SimpleQuote(DateTimeToSqlDate(TDateTime(Src^), TIME_MODE)); - fldTIMESTAMP: Result := SimpleQuote(DateTimeToSqlDate(TDateTime(Src^), TIMESTAMP_MODE)); -{$IFDEF DELPHI_12} - fldPOINT: Result := SimpleQuote(PointToSQLPoint(TPSQLPoint(Src^))); - fldCIRCLE: Result := SimpleQuote(CircleToSQLCircle(TPSQLCircle(Src^))); - fldBOX: Result := SimpleQuote(BoxToSQLBox(TPSQLBox(Src^))); - fldLSEG: Result := SimpleQuote(LSegToSQLLSeg(TPSQLLSeg(Src^))); - fldRANGE: Result := SimpleQuote(RangeToSQLRange(TPSQLRange(Src^), NativeType)); -{$ENDIF DELPHI_12} - end; -end; - -function TPSQLField.GetFieldDefault : string;//mi -begin - Result := string(FValCheck.aDefVal); -end; - -procedure TPSQLField.SetFieldDefault(aStr : string);//mi -begin - FValCheck.aDefVal := aStr; -end; - -////////////////////////////////////////////////////////// -// TPSQLFields -////////////////////////////////////////////////////////// - -function TPSQLFields.AddField(P: FldDesc; P1: VCHKDesc; FNum, LType, - LSize: integer; isArray: Boolean): TPSQLField; -begin - Result := Add as TPSQLField; - Result.Description := P; - Result.ValCheck := P1; - Result.FieldNumber := FNum; - Result.NativeType := LType; - case Result.NativeType of - FIELD_TYPE_BYTEA: Result.NativeBLOBType := nbtBytea; - FIELD_TYPE_OID: Result.NativeBLOBType := nbtOID - else - Result.NativeBLOBType := nbtNotBlob; - end; - Result.NativeSize := LSize; //Max(LSize, Result.FieldLength); - Result.FieldArray := isArray; -end; - -constructor TPSQLFields.Create(Table : TNativeDataSet); -begin - Inherited Create(TPSQLField); - FTable := Table; -end; - - -function TPSQLFields.GetField(Index : Integer) : TPSQLField; -var - LocType : Integer; - LocSize : Integer; - LocArray : Boolean; - Desc : FldDesc; - ValCheck : VCHKDesc; -begin - if ( Count >= Index ) and ( Index > 0 ) then - Result := TPSQLField(Items[Index-1]) else - begin - if not ((Index > 0) and (FTable <> nil)) then raise EPSQLException.CreateBDE(DBIERR_INVALIDRECSTRUCT); - FTable.GetNativeDesc(Index, Desc, ValCheck, LocType, LocSize, LocArray); - Result := TPSQLField.CreateField(Self, Desc, ValCheck, Index, LocType, LocSize,LocArray); - end; -end; - -function TPSQLFields.GetNativeConnect: TNativeConnect; -begin - Result := FTable.Connect; -end; - - -procedure TPSQLFields.SetFields(PRecord : Pointer); -var - i : integer; -begin - For i := 1 to Count do - begin - With Field[i] do - begin - Buffer := PRecord; - FieldChanged := FALSE; - FieldNull := TRUE; - end; - end; -end; - -function TPSQLFields.FieldNumberFromName(SearchName : PChar) : Integer; -var - I : Integer; -begin - Result := 0; - For i := 1 to Count do - begin - With GetField( i ) do - begin - if (StrIComp(SearchName, PChar(FieldName)) = 0) then - begin - Result := Integer(FieldNumber); - Exit; - end; - end; - end; -end; - -////////////////////////////////////////////////////////// -//constructor : TPSQLIndex.CreateIndex -//Description : constructor CreateIndex -////////////////////////////////////////////////////////// -//Input : Owner: TCollection -// P: pIDXDesc -////////////////////////////////////////////////////////// -constructor TPSQLIndex.CreateIndex(Owner : TCollection; P : pIDXDesc); -begin - Create(Owner); - FDesc := P^; -end; - -constructor TPSQLIndexes.Create(Table : TNativeDataSet); -begin - Inherited Create(TPSQLIndex); - FTable := Table; -end; - -function TPSQLIndexes.GetIndex(Index : Integer) : TPSQLIndex; -begin - Result := nil; - if ( Count >= Index ) and ( Index > 0 ) then Result := TPSQLIndex(Items[Index-1]); -end; - -function TPSQLIndexes.FindByName(Name :String): TPSQLIndex; -var - I : Integer; -begin - Result := nil; - for i := 0 to Count-1 do - begin - if (CompareText(TPSQLIndex(Items[I]).IndexName, Name) = 0) then - begin - Result := TPSQLIndex(Items[I]); - Exit; - end; - end; -end; - -function TPSQLIndexes.SetIndex(Name,Fields : String;aPrimary,aUnique,aDesc : Boolean): integer; -var - Item : TPSQLIndex; - I,K,J : Integer; - FldLen : integer; - FieldList : TStrings; - - //this is used, because Postgres didn't delete dropped columns, - //but only mark them as deleted. See pg_attribute for details - function GetLogicalIndexByPhysical(const PhNum: integer):integer; - var I: integer; - begin - Result := 0; - For I:=0 to FTable.FieldCount-1 do - if FTable.FieldPosInTable(I) = PhNum then - begin - Result := I+1; - Break; - end; - end; - -begin - Result := -1; - Item := FindByName(Name); - if not Assigned(Item) then - begin - Item := TPSQLIndex(Add); - Item.IndexNumber := Item.Index+1; - Item.IndexName := Name; - end; - - Item.Primary := aPrimary; - Item.Unique := aUnique; - Item.Descending := aDesc; - Item.FDesc.bMaintained := True; - FieldList := TStringList.Create; - try - FieldList.CommaText := Fields; - for J := 0 to FieldList.Count-1 do - begin - I := StrToIntDef(FieldList[J],0); - if FTable.FConnect.GetserverVersionAsInt >= 070400 then - I := GetLogicalIndexByPhysical(I); - - if I = 0 then //we have index built on expressions. - begin - Item.Free; - Exit; - end; - FldLen := FTable.FFieldDescs.GetField(I).NativeSize; //utf indices built on varchar need 2 bytes per character - Item.FldsInKey := Item.FldsInKey+1; - Item.BlockSize := Item.BlockSize+FldLen; - Item.KeyLen := Item.BlockSize+Item.FldsInKey; - K :=Item.FldsInKey; - Item.FDesc.aiKeyFld[K-1] := I; - Result := Item.IndexNumber; - end; - finally - FieldList.Free; - end; -end; - -function TPSQLIndexes.FieldNumberFromName( SearchName : PChar ) : Integer; -var - I : Integer; -begin - Result := 0; - if FTable.FFieldDescs.Count = 0 then FTable.InitFieldDescs; - For i := 1 to FTable.FFieldDescs.Count do - begin - With FTable.FFieldDescs.GetField(i) do - begin - if (StrIComp(SearchName, PChar(FieldName))= 0) then - begin - Result := Integer(FieldNumber); - Exit; - end; - end; - end; -end; - -////////////////////////////////////////////////////////// -//constructor : TPSQLNative.CreateNative -//Description : constructor CreateNative -////////////////////////////////////////////////////////// -//Input : Owner: TCollection -////////////////////////////////////////////////////////// -constructor TPSQLNative.CreateNative(Owner : TCollection; P : PPGFIELD_INFO); -begin - Create(Owner); - FDesc := P^; -end; - - -constructor TPSQLNatives.Create(Table : TNativeDataSet); -begin - Inherited Create(TPSQLNative); - FTable := Table; -end; - -function TPSQLNatives.GetNative(Index : Integer) : TPSQLNative; -begin - Result := nil; - if ( Count >= Index ) and ( Index > 0 ) then Result := TPSQLNative(Items[Index-1]); -end; - -procedure TPSQLNatives.SetNative(aIndex : Integer; aName : String; aType, aSize, aMaxSize, aTypMod : Integer); -var - Item : TPSQLNative; -begin - Item := TPSQLNative(Add); - Item.NativeNumber := aIndex; - Item.NativeName := aName; - Item.NativeType := aType; - Item.NativeSize := aSize; - Item.NativeMaxSize := aMaxSize; - Item.NativeTypMod := aTypMod; -end; - - -////////////////////////////////////////////////////////// -//Description : TPSQLFilter impementation -////////////////////////////////////////////////////////// -constructor TPSQLFilter.Create(Owner : TNativeDataSet; AClientData : Longint; Exp : pCANExpr; pfFilt : pfGENFilter); -begin - Inherited Create; - FDataset := Owner; - FClientData := AClientData; - if Assigned(Exp) then - begin - FExprSize := CANExpr(Exp^).iTotalSize; - if FExprSize > 0 then - begin - GetMem(FExpression, FExprSize); - if Assigned(FExpression) then Move(Exp^, FExpression^, FExprSize); - end; - end; - FPfFilter:= pfFilt; - FActive:= FALSE; -end; - -Destructor TPSQLFilter.Destroy; -begin - if (FExprSize > 0) and Assigned(FExpression) then FreeMem(FExpression, FExprSize); - Inherited Destroy; -end; - -function TPSQLFilter.GetFilterResult(PRecord : Pointer) : Variant; -var - I : Integer; -begin - if FActive then - begin - FRecBuff := PRecord; - if Assigned(FpfFilter) then - begin - i := 0; - try - i := FpfFilter(FClientData, FRecBuff, Longint(0)); - finally - result := i <> 0; - end; - end else - begin - if Assigned(FExpression) then - begin - try - Result := CalcExpression(GetNodeByOffset(NodeStart)); - if Result = Null then Result := False; - except - Result := FALSE; - end; - end; - end; - end else Result := False; -end; - -function TPSQLFilter.GetNodeStart : Integer; -begin - Result := FExpression.iNodeStart; -end; - -function TPSQLFilter.GetLiteralPtr(AOffset: Word): Pointer; -var - i : word; -begin - i := CANExpr(FExpression^).iLiteralStart + AOffset; - Result := @MemPtr(FExpression)^[i]; -end; - -function TPSQLFilter.GetNodeByOffset(AOffSet : Integer) : PCanNode; -begin - Result := pCanNode(DACPointerInt(FExpression)+AOffset); -end; - -function TPSQLFilter.CalcExpression(ANode : PCanNode) : Variant; -Var - FldType : TFldType; -begin - Case pCanHdr(ANode).nodeClass Of - PSQLTypes.nodeUNARY : Result := UnaryNode(pCANUnary(ANode)); - PSQLTypes.nodeBINARY : Result := BinaryNode(pCANBinary(ANode)); - PSQLTypes.nodeCOMPARE : Result := CompareNode(pCANCompare(ANode)); - PSQLTypes.nodeFIELD : Result := FieldNode(pCANField(ANode)); - PSQLTypes.nodeCONST : Result := PerformCanConst(PCANConst(ANode), - GetLiteralPtr(PCANConst(ANode).iOffset), FldType); - PSQLTypes.nodeLISTELEM : Result := ListOfValues(pCANListElem(ANode)); - else - result := Null; - End; -end; - -function TPSQLFilter.ListOfValues(ANode : pCANListElem) : Variant; -Var - I : Integer; - CurNode : pCANListElem; -begin - CurNode := ANode; - I := 0; - While True Do - begin - Inc(I); - if CurNode^.iNextOffset = 0 Then break; - CurNode := pCanListElem(GetNodeByOffset(NodeStart + CurNode^.iNextOffset)); - end; - Result := varArrayCreate([1, I], varVariant); - I := 1; - While True Do - begin - Result[ I ] := CalcExpression(PCanNode(GetNodeByOffset(NodeStart + ANode^.iOffset))); - if ANode^.iNextOffset = 0 Then break; - ANode := pCanListElem(GetNodeByOffset(NodeStart + ANode^.iNextOffset)); - Inc(I); - end; -end; - -function TPSQLFilter.PerformLikeCompare(Const Value, Mask : String; CaseSen : Boolean) : Boolean; -begin - Result := PSQLExtMask.MatchesMask(Value, Mask, CaseSen); -end; - -function TPSQLFilter.PerformInCompare(AOp1, AOp2 : Variant) : Boolean; -Var - Save : Variant; - I, Top : Integer; -begin - if varType(AOp1) = varArray then - begin - Save := AOp2; - AOp2 := AOp1; - AOp1 := Save; - end; - Result := True; - Top := VarArrayHighBound(AOp2, 1); - For I := VarArrayLowBound(AOp2, 1) to Top do - if AOp1 = AOp2[I] then Exit; - Result := False; -end; - -function TPSQLFilter.UnaryNode( ANode : PCANUnary ) : Variant; -begin - With ANode^ Do Result := PerformCANOp(canOp, GetNodeValue(iOperand1), UnAssigned); -end; - -function TPSQLFilter.BinaryNode(ANode : PCANBinary) : Variant; -begin - With ANode^ Do Result := PerformCANOp(canOp, GetNodeValue(iOperand1), GetNodeValue(iOperand2)); -end; - -function TPSQLFilter.CompareNode(ANode : PCANCompare) : Variant; -Var - Op1, Op2 : Variant; -begin - Op1 := GetNodeValue(Anode^.iOperand1); - Op2 := GetNodeValue(Anode^.iOperand2); - if varIsNull(Op1) Or varIsEmpty(Op1) Then Op1 := ''; - if varIsNull(Op2) Or varIsEmpty(Op2) Then Op2 := ''; - if ANode.canOp = canLike then - Result := PerformLikeCompare(Op1, Op2, not ANode^.bCaseInsensitive) else - begin - Result := Search(Op1, Op2, OEMConv, not Anode^.bCaseInsensitive, Anode^.iPartialLen); - if Anode^.canOp = canNE then Result := not Result; - end; - -end; - -function TPSQLFilter.FieldNode(ANode : pCANField) : Variant; -Var - Field : TPSQLField; - blank : Boolean; - Dest : array[0..MAX_CHAR_LEN] of Char; - TimeStamp : TTimeStamp; - DateD : Double; -begin - Result := Null; - Field := FDataset.Fields[ANode.iFieldNum]; - FDataSet.NativeToDelphi(Field, FrecBuff, @Dest, blank); - if blank then Exit; - case Field.FieldType of - fldINT16: Result := PSmallInt(@Dest)^; - fldUINT16:Result := PWord(@Dest)^; - fldINT32: Result := PLongInt(@Dest)^; - fldUINT32:Result := PLongInt(@Dest)^; - {$IFDEF DELPHI_6} - fldINT64: Result := PInt64(@Dest)^; - {$ENDIF} - fldFLOAT: Result := PDouble(@Dest)^; - fldZSTRING: - {$IFDEF DELPHI_12} - if FDataset.FConnect.IsUnicodeUsed then - Result := string(PChar(@Dest)) - else - {$ENDIF} - Result := string(PAnsiDACChar(@Dest)); - fldUUID: Result := string(PAnsiDACChar(@Dest)); - fldBOOL : Result := PWordBool(@Dest)^; - fldDATE : begin - TimeStamp.Date := PLongWord(@Dest)^; - TimeStamp.Time := 0; - Result := SysUtils.Time+Trunc(TimeStampToDateTime(TimeStamp) + 1E-11); - end; - fldTIME : begin - TimeStamp.Time := PLongWord(@Dest)^; - TimeStamp.Date := 0; - Result := SysUtils.Date+TimeOf(TimeStampToDateTime(TimeStamp)); - end; - fldTIMESTAMP : begin - DateD := PDouble(@Dest)^; - Result := TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF}(DateD))); - end; - else Result := NULL; - end; -end; - -function TPSQLFilter.GetNodeValue(AOffSet : Integer) : Variant; -begin - Result := CalcExpression(GetNodeByOffset(NodeStart + AOffset)); -end; - -function TPSQLFilter.PerformCANOp(AOperator : CANOp; AOp1, AOp2 : Variant) : Variant; -begin - Case AOperator of - canNOTDEFINED : Result := Null; - canISBLANK : Result := VarIsNull(AOp1); - canNOTBLANK : Result := not VarIsNull(AOp1); - canNOT : Result := not AOp1; - canEQ : Result := AOp1 = AOp2; - canNE : Result := AOp1 <> AOp2; - canGT : Result := AOp1 > AOp2; - canLT : Result := AOp1 < AOp2; - canGE : Result := AOp1 >= AOp2; - canLE : Result := AOp1 <= AOp2; - canAND : Result := AOp1 and AOp2; - canOR : Result := AOp1 or AOp2; - canMinus : Result := -AOp1; - canADD : Result := AOp1+AOp2; - canSUB : Result := AOp1-AOp2; - canMUL : Result := AOp1*AOp2; - canDIV : Result := AOp1 / AOp2; - canMOD : Result := AOp1 mod AOp2; - canREM : Result := AOp1 mod AOp2; - canSUM : Result := Null; - canCONT : Result := Null; - canLike : Result := PerformLikeCompare(AOp1,AOp2,True); - canIN : Result := PerformInCompare(AOp1,AOp2); - canUPPER : Result := AnsiUpperCase(AOp1); - canLOWER : Result := AnsiLowerCase(AOp1); - canASSIGN : Result := VarIsNull(AOp1); - Else Result := Null; - end; -end; - -function TPSQLFilter.PerformCanConst(ANode:PCANConst; ValuesStart : Pointer; Var FldType : TFldType) : Variant; - -function _PerformCanConst( ANode : PCANConst; ValuePtr : Pointer; Var FldType : TFldType) : Variant; -Var - Offs : DACPointerInt; - TimeStamp : TTimeStamp; - DateData : Double; - S: String; -{$IFDEF DELPHI_12} - Len: word; - buffer: PChar; -{$ENDIF} -begin - With ANode^ Do - begin - Offs := DACPointerInt(ValuePtr); - FldType := FT_UNK; - Result := Null; - Case iType Of - fldZSTRING : begin - S:= string(PAnsiDACChar(Offs)); - Result := S; - FldType := FT_STRING; - end; -{$IFDEF DELPHI_12} - fldUNICODE : begin - buffer := ValuePtr; - Len := Word(buffer[0]); - Inc(buffer); - SetLength(S, Len div 2); - if Len > 0 then - S := Copy(buffer, 1, Len div 2); - Result := S; - FldType := FT_STRING; - end; -{$ENDIF} - fldDATE : begin - TimeStamp.Date := PLongWord( Offs )^; - TimeStamp.Time := 0; - Result := SysUtils.Time+ Trunc(TimeStampToDateTime(TimeStamp) + 1E-11); - FldType := FT_DATE; - end; - fldBOOL : begin - Result := PWordBool( Offs )^; - FldType := FT_BOOL; - end; - - fldINT16 : begin - Result := PSmallInt( Offs )^; - FldType := FT_INT; - end; - fldINT32 : begin - Result := PInteger( Offs )^; - FldType := FT_INT; - end; - {$IFDEF DELPHI_6} - fldINT64 : begin - Result := Pint64( Offs )^; - FldType := FT_INT; - end; - {$ENDIF} - fldFLOAT : begin - Result := PDouble( Offs )^; - FldType := FT_FLOAT; - end; - fldTIME : begin - TimeStamp.Time := PLongWord( Offs )^; - TimeStamp.Date := 0; - Result := SysUtils.Date+TimeOf(TimeStampToDateTime( TimeStamp )); - FldType := FT_TIME; - end; - - fldTIMESTAMP : begin - DateData := PDouble( Offs )^; - Result := TimeStampToDateTime( MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF} (DateData) ) ); - FldType := FT_DATETIME; - end; - fldUINT16 : begin - Result := PWord( Offs )^; - FldType := FT_INT; - end; - fldUINT32 : begin - Result := PInteger( Offs )^; - FldType := FT_INT; - end; - end; - end; -end; -begin - Result:=_PerformCanConst(ANode,ValuesStart,FldType); -end; - -function TPSQLFilter.TimeOf(const ADateTime: TDateTime): TDateTime; -var - Hour, Min, Sec, MSec: Word; -begin - DecodeTime(ADateTime, Hour, Min, Sec, MSec); - Result := EncodeTime(Hour, Min, Sec, MSec); -end; - -{$O-} -constructor TNativeDataSet.Create(PSQL : TNativeConnect; - //Container : TContainer; - AnOptions: TPSQLDatasetOptions; - AName, IndexName : string; - Index : Word; - Limit, Offset : Integer; - ASystem : Boolean = False); -begin - Inherited Create; - FStatement := nil; - FFilters := TContainer.Create; - if IndexName <> '' then FIndexName := IndexName; - FFieldDescs := TPSQLFields.Create(Self); - FIndexDescs := TPSQLIndexes.Create(Self); - FNativeDescs := TPSQLNatives.Create(Self); - FKeyNumber := 0; - FPrimaryKeyNumber := 0; - AutoReExec := True; - FConnect := PSQL; - FOptions := AnOptions; - FOpen := False; - FRecSize:=-1; - FLimit := Limit; - FOffset := Offset; - StandartClause := TStringList.Create; - OrderClause := TStringList.Create; - RangeClause := TStringList.Create; - LimitClause := TStringList.Create; - TableName := string(AName); - MasterCursor := nil; - FSystemNeed := ASystem; - IsQuery := False; - FPreventRememberBuffer := False; //mi:2008-08-27 -end; - -Destructor TNativeDataSet.Destroy; -begin - MasterCursor := nil; - CloseTable; - ClearIndexInfo; - StandartClause.Free; - OrderClause.Free; - RangeClause.Free; - limitClause.Free; - FNativeDescs.Free; - FIndexDescs.Free; - FFieldDescs.Free; - FFilters.Free; - Inherited Destroy; -end; - -////////////////////////////////////////////////////////// -// PROTECTED METHODS // -////////////////////////////////////////////////////////// -procedure TNativeDataSet.SetBufferAddress(P : Pointer); -begin - FCurrentBuffer := P; -end; - -procedure TNativeDataSet.SetInternalBuffer(Buffer : Pointer); -begin - if not FPreventRememberBuffer then //mi:2008-08-27 check if we need to remember buffer - BufferAddress := Buffer; -end; - -function TNativeDataSet.GetInternalBuffer : Pointer; -begin - Result := FInternalBuffer; -end; - -procedure TNativeDataSet.SetCurrentBuffer(PRecord : Pointer); -begin - FCurrentBuffer := PRecord; -end; - -function TNativeDataSet.GetCurrentBuffer : Pointer; -begin - Result := FCurrentBuffer; -end; - -function TNativeDataSet.FieldOffset(iField: Integer): integer; -var - i: SmallInt; -begin - Result:=0; - if not ((iField>=1) or (iField<=FieldCount)) then raise EPSQLException.CreateBDE(DBIERR_INVALIDPARAM); - Dec(iField); - Dec(iField); - for i:=0 to iField do - begin - case FieldType(I) of - FIELD_TYPE_INT2, - FIELD_TYPE_BOOL: Inc(Result,SizeOf(SmallInt)); - FIELD_TYPE_OID, - FIELD_TYPE_TEXT, - FIELD_TYPE_BYTEA: Inc(Result,SizeOf(TBlobItem)); - FIELD_TYPE_INT4: Inc(Result,SizeOf(LongInt)); - FIELD_TYPE_INT8: Inc(Result,SizeOf(Int64)); - FIELD_TYPE_DATE, - FIELD_TYPE_TIME: Inc(Result,SizeOf(TDateTime)); - FIELD_TYPE_TIMESTAMP: Inc(Result,SizeOf(TDateTime)); - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: Inc(Result, SizeOf(Double)); - FIELD_TYPE_NUMERIC: Inc(Result, SizeOf(TBcd)); - else - Inc(Result,FieldMaxSizeInBytes(I)); - end; - end; -end; - -function TNativeDataSet.GetBookMarkSize : Integer; -begin - Result := Sizeof(TPSQLBookMark); -end; - -procedure TNativeDataSet.SetBufBookmark; -Var - Buffer : Pointer; -begin - if (CurrentBuffer <> nil) and (FBookOfs > 0) then - begin - Buffer := CurrentBuffer; - Buffer := Pointer(Int64(Buffer) + FBookOfs); - GetBookMark(Buffer); - end; -end; - -function TNativeDataSet.GetRecordNumber: Longint; -begin - Result := RecNo; -end; - -procedure TNativeDataSet.SetRecordNumber(RecNom : Longint); -var - Original: LongInt; -begin - Original := RecNom; - if RecNom < 0 then - begin - RecNom := RecordCount; - Dec(RecNom); - try - if RecordState <> tsEmpty then CurrentRecord(RecNom); - except - end; - end else - if RecordState <> tsEmpty then CurrentRecord(RecNom); - Recno := Original; -end; - -function TNativeDataSet.GetRecCount: LongInt; -begin - Result := 0; - if not Assigned(FStatement) then Exit; - Result := PQntuples(FStatement); -end; - -procedure TNativeDataSet.GetRecordCount( var iRecCount : integer ); -var - P : Pointer; - Buff : Pointer; - Marked : Boolean; -begin - if not FFilterActive then - iRecCount := GetRecCount() - else - begin - iRecCount := 0; - GetMem(Buff, GetWorkBufferSize); - try - - GetMem(P, BookMarkSize); - try - try - GetBookMark(P); - Marked := true; - except - on E:EPSQLException do - Marked := false; - end; - - SetToBegin(); - try - repeat - GetNextRecord(dbiNOLOCK, Buff, nil); - Inc(iRecCount); - until false; - except - on E:EPSQLException do; - end; - - if Marked then - SetToBookMark(P) - else - SetToBegin; - finally - FreeMem(P, BookMarkSize); - end; - finally - FreeMem(Buff, GetWorkBufferSize); - end; - end; -end; - -procedure TNativeDataSet.CheckFilter(PRecord : Pointer); -var - P : Pointer; - B : Boolean; - aSize: integer; -begin - if PRecord <> nil then - begin - if FFilterActive then - While not FilteredRecord(PRecord) do - begin - InternalBuffer := PRecord; - if FLastDir <> tdPrev then NextRecord else PrevRecord; - end; - end else - begin - if FFilterActive then - begin - aSize := GetWorkBufferSize; - P := AllocMem(aSize); - try - InternalBuffer := P; - InternalReadBuffer; - B := FilteredRecord(P); - While not B do - begin - InternalBuffer := P; - if FLastDir <> tdPrev then - NextRecord() - else - PrevRecord(); - B := FilteredRecord(P); - end; - finally - FreeMem(P,aSize); - InternalBuffer := nil; - end; - end; - end; -end; - -procedure TNativeDataSet.FirstRecord; -begin - RecNo := 0; - if RecordCount = 0 then - raise EPSQLException.CreateBDE(DBIERR_EOF); - InternalReadBuffer; - SetBufBookmark; - MonitorHook.SQLFetch(Self); -end; - -procedure TNativeDataSet.LastRecord; -begin - if (dsoFetchOnDemand in Options) and not FFetched then - FetchRecords(MaxInt); - RecNo := RecordCount - 1; - if RecNo < 0 then - raise EPSQLException.CreateBDE(DBIERR_EOF); - InternalReadBuffer; - SetBufBookmark; - MonitorHook.SQLFetch(Self); -end; - -procedure TNativeDataSet.NextRecord(); -begin - Inc(RecNo); - if (RecNo >= RecordCount) and (dsoFetchOnDemand in Options) and not FFetched then - FetchRecords(); - if RecNo >= RecordCount then - raise EPSQLException.CreateBDE(DBIERR_EOF); - InternalReadBuffer; - SetBufBookmark; - MonitorHook.SQLFetch(Self); -end; - -procedure TNativeDataSet.PrevRecord(); -begin - if RecNo >= RecordCount then - raise EPSQLException.CreateBDE(DBIERR_BOF); - if RecNo > 0 then - Dec(RecNo) - else - raise EPSQLException.CreateBDE(DBIERR_BOF); - InternalReadBuffer; - SetBufBookmark; - MonitorHook.SQLFetch(Self); -end; - -procedure TNativeDataSet.CurrentRecord(ARecNo : Longint); -begin - RecNo := ARecNo; - if RecNo <= -1 then raise EPSQLException.CreateBDE(DBIERR_BOF); - SetBufBookmark; - InternalReadBuffer; -end; - -procedure TNativeDataSet.GetWorkRecord(eLock: DBILockType;PRecord: Pointer); -var - P : TPSQLBookMark; -begin - GetBookMark(@P); - CheckParam(@P=nil,DBIERR_INVALIDPARAM); - if not FIsLocked then - begin - SetToBookMark(@P); - if eLock = dbiWRITELOCK then LockRecord(eLock); - RecordState := tsPos; - end; -end; - -procedure TNativeDataSet.LockRecord(eLock : DBILockType); -begin - FIsLocked := (eLock <> dbiNOLOCK); -end; - -function TNativeDataSet.FilteredRecord(PRecord : Pointer) : Boolean; -var - P : TPSQLFilter; - I : Integer; -begin - Result := TRUE; - if FFilterActive then - begin - For i := 0 to FFilters.Count-1 do - begin - P := FFilters.Items[i]; - if P.Active and not P.GetFilterResult(PRecord) then - begin - Result := FALSE; - Exit; - end; - end; - end; -end; - -procedure TNativeDataSet.UpdateFilterStatus; -Var - P : TPSQLFilter; - I : Integer; -begin - For i := 0 to FFilters.Count-1 do - begin - P := FFilters.Items[i]; - if (P <> NIL) and (P.Active) then - begin - FFilterActive := TRUE; - Exit; - end; - end; - FFilterActive := FALSE; -end; - -procedure TNativeDataSet.NativeToDelphi(P: TPSQLField; PRecord: Pointer; pDest: Pointer; var bBlank: Boolean); -begin - CheckParam(PRecord = nil, DBIERR_INVALIDPARAM); - P.Buffer := PRecord; - bBlank := P.FieldNull; - if not bBlank and Assigned(pDest) then - AdjustNativeField(P, P.FieldValue, pDest, bBlank); -end; - -procedure TNativeDataSet.DelphiToNative(P: TPSQLField;PRecord: Pointer;pSrc: Pointer); -begin - if pSrc <> nil then AdjustDelphiField(P, pSrc, PAnsiDACChar(P.Data) + P.FieldNumber - 1); -end; - -procedure TNativeDataSet.CheckParam(Exp : Boolean;BDECODE : Word); -begin - if Exp then raise EPSQLException.CreateBDE(BDECODE); -end; - -///////////////////////////////////////////////////////////////////// -// PUBLIC METHODS // -///////////////////////////////////////////////////////////////////// -procedure TNativeDataSet.GetRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); -begin - InternalBuffer := PRecord; - Case RecordState of - tsPos: - begin - GetWorkRecord(eLock,PRecord); - try - CheckFilter(PRecord); - except - On E:EPSQLException do - begin - if FReRead then - begin - FReRead := FALSE; - RecordState := tsNoPos; - GetNextRecord( eLock, PRecord, pRecProps ); - end - else - begin - if eLock = dbiWRITELOCK then FIsLocked := FALSE; - raise; - end; - end; - end; - if pRecProps <> nil then - begin - pRecProps^.iPhyRecNum := RecNo+1; - pRecProps^.iSeqNum := RecNo+1; - end; - end; - tsFirst: raise EPSQLException.CreateBDE(DBIERR_EOF); - tsLast: raise EPSQLException.CreateBDE(DBIERR_BOF); - tsEmpty: - begin - try - GetNextRecord( eLock, PRecord, pRecProps ); - except - On E:EPSQLException do - begin - try - GetPriorRecord( eLock, PRecord, pRecProps ); - except - On E:EPSQLException do - begin - RecordState := tsNoPos; - GetNextRecord( eLock, PRecord, pRecProps ); - end; - end; - end; - end; - end; - else raise EPSQLException.CreateBDE(DBIERR_NOCURRREC); - end; -end; - -procedure TNativeDataSet.GetNextRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); -begin - FLastDir := tdNext; - InternalBuffer := PRecord; - Case RecordState of - tsPos, - tsEmpty: NextRecord; - tsFirst, - tsNoPos: FirstRecord; - else raise EPSQLException.CreateBDE(DBIERR_EOF); - end; - CheckFilter(PRecord); - if eLock <> dbiNOLOCK then GetRecord(eLock, PRecord, pRecProps); - if pRecProps <> nil then - begin - pRecProps^.iPhyRecNum := RecNo+1; - pRecProps^.iSeqNum := RecNo+1; - end; - RecordState := tsPos; -end; - -procedure TNativeDataSet.GetPriorRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); -begin - FLastDir := tdPrev; - InternalBuffer := PRecord; - case RecordState of - tsPos, - tsEmpty: PrevRecord; - tsLast, - tsNoPos: LastRecord; - else raise EPSQLException.CreateBDE(DBIERR_BOF); - end; - CheckFilter(PRecord); - if eLock <> dbiNOLOCK then GetRecord(eLock, PRecord, pRecProps); - if pRecProps <> nil then - begin - pRecProps^.iPhyRecNum := RecNo+1; - pRecProps^.iSeqNum := RecNo+1; - end; - RecordState := tsPos; -end; - -procedure TNativeDataSet.AddFilter(iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr;pfFilter: pfGENFilter;var hFilter: hDBIFilter); -var - P : TPSQLFilter; -begin - P := TPSQLFilter.Create(Self,iClientData,pcanExpr,pfFilter); - FFilters.Insert(P); - UpdateFilterStatus; - hFilter := hDBIFilter(P); -end; - -procedure TNativeDataSet.DropFilter(hFilter: hDBIFilter); -var - Count: Integer; -begin - if hFilter = nil then - FFilters.FreeAll - else - begin - Count := FFilters.Count; - FFilters.Delete(hFilter); - if Count <> FFilters.Count then - begin - {$IFDEF NEXTGEN} - TPSQLFilter(hFilter).DisposeOf; - {$ELSE} - TPSQLFilter(hFilter).Free; - {$ENDIF} - UpdateFilterStatus; - end; - end; -end; - -procedure TNativeDataSet.ActivateFilter(hFilter: hDBIFilter); -var - i : Integer; - P : TPSQLFilter; - Found : Boolean; -begin - Found := FALSE; - For i := 0 to FFilters.Count-1 do - begin - P := FFilters.Items[i]; - if (hFilter = nil) or (hFilter = hDBIFilter(P)) then - begin - P.Active := TRUE; - FFilterActive := TRUE; - Found := TRUE; - end; - end; - if not Found and (hFilter <> nil) then raise EPSQLException.CreateBDE(DBIERR_NOSUCHFILTER); -end; - -procedure TNativeDataSet.DeactivateFilter(hFilter: hDBIFilter); -var - i : Integer; - P : TPSQLFilter; -begin - if hFilter = nil then - begin - For i := 0 to FFilters.Count-1 do - begin - P := FFilters.Items[i]; - P.Active := FALSE; - end; - FFilterActive := FALSE; - end else - begin - if TPSQLFilter( hFilter ).Active then - begin - TPSQLFilter( hFilter ).Active := FALSE; - UpdateFilterStatus; - end; - end; -end; - -procedure TNativeDataSet.SetToRecord(RecNo : LongInt); -begin - if RecNo < 0 then - begin - try - if RecordState <> tsEmpty then CurrentRecord(RecNo); - except - end; - end - else - if RecordState <> tsEmpty then CurrentRecord(RecNo); -end; - -procedure TNativeDataSet.SetToBookmark(P : Pointer); -begin - CheckParam(P = nil, DBIERR_INVALIDPARAM); - - if TPSQLBookMark(P^).Position > 0 then - begin - if TPSQLBookMark(P^).Position > RecordCount then - LastRecord() - else - RecordNumber := TPSQLBookMark(P^).Position - 1 - end - else - FirstRecord; - - RecordState := tsPos; -end; - -procedure TNativeDataSet.OpenTable; -var - sql_stmt : string; - - procedure InternalOpen; - begin - if SQLQuery = '' then - if StandartClause.Count > 0 then - sql_stmt := GetSQLClause - else - raise EPSQLException.CreateBDE(DBIERR_QRYEMPTY) - else - sql_stmt := Trim(SQLQuery); - if dsoFetchOnDemand in Options then - if _PQSendQuery(FConnect, sql_stmt) = 1 then - if PQsetSingleRowMode(FConnect.Handle) = 1 then - FStatement := PQgetResult(FConnect.Handle) - else - DatabaseError('Cannot switch to dsoFetchOnDemand mode') - else - FConnect.CheckResult - else - FStatement := _PQExecute(FConnect, sql_stmt); - if Assigned(FStatement) then - begin - try - FConnect.CheckResult(FStatement); - MonitorHook.SQLExecute(Self, True); - except - MonitorHook.SQLExecute(Self, False); - CloseTable; - raise; - end; - FOpen := True; - if FFieldDescs.Count = 0 then - InitFieldDescs; - RecNo := 0; - end else - begin - FConnect.CheckResult; - PQclear(FStatement); - end; - end; - -begin - if FOpen then CloseTable; - FAffectedRows := 0; - FOpen := False; - FFetched := False; - sql_stmt := ''; - - try - if (StandartClause.Count = 0) and (SQLQuery = '') then - begin - isQuery := False; - StandartClause.Add('SELECT * FROM ' + TableName); - if FOpen then ClearIndexInfo; - limitClause.Add('LIMIT 1'); - FSystemNeed := true; - InternalOpen; - FSystemNeed := False; - limitClause.Clear; - if FLimit > 0 then - LimitClause.Add(Format('LIMIT %s',[IntToStr(FLimit)])); - if FOffset > 0 then - LimitClause.Add(Format('OFFSET %s',[IntToStr(FOffset)])); - if IndexCount > 0 then - begin - if FPrimaryKeyNumber = 0 then FPrimaryKeyNumber := 1; - SwitchToIndex(FIndexName, '', 0, False ); - end - else - begin - PQClear(FStatement); - InternalOpen; - end; - Exit; - end; - FLastOperationTime := GetTickCount; - InternalOpen; - FLastOperationTime := GetTickCount - FLastOperationTime; - if (KeyNumber = 0) then - begin - if FPrimaryKeyNumber <> 0 then - GetIndexDesc(FPrimaryKeyNumber, FKeyDesc) - else - if IndexCount > 0 then - if FPrimaryKeyNumber <> 0 then - GetIndexDesc(FPrimaryKeyNumber, FKeyDesc) - else - GetIndexDesc(1, FKeyDesc); - end; - finally - SetLength(FFieldMinSizes,0); - FFieldTypType := ''; - if FSortingFields > '' then - SortBy(FSortingFields); - end; -end; - -procedure TNativeDataSet.GetField(FieldNo: Word;PRecord: Pointer;pDest: Pointer;var bBlank: Boolean); -var - T : TPSQLField; -begin - CheckParam(PRecord = nil, DBIERR_INVALIDPARAM); - T := FFieldDescs[FieldNo]; - T.Buffer := PRecord; - if Assigned(pDest) then - NativeToDelphi(T, PRecord, pDest, bBlank) - else - bBlank := T.FieldNull; -end; - -procedure TNativeDataSet.PutField(FieldNo: Word;PRecord: Pointer;pSrc: Pointer); -var - T : TPSQLField; -begin - CheckParam(PRecord=nil,DBIERR_INVALIDPARAM); - T := FFieldDescs[FieldNo]; - T.Buffer := PRecord; - DelphiToNative(T, PRecord, pSrc); - T.FieldChanged := TRUE; - T.FieldNull := pSrc = nil; -end; - -procedure TNativeDataSet.CloseTable; -begin - FAffectedRows := 0; - RecNo := -1; - if not FConnect.FLoggin then exit; - if FStatement <> nil then PQclear(FStatement); - FStatement := nil; - FOpen := False; - SetLength(FFieldMinSizes,0); - Finalize(FKeyDesc); - FFieldTypType := ''; -end; - -procedure TNativeDataSet.GetBookMark( P : Pointer ); -begin - {$IFNDEF NEXTGEN} - ZeroMemory(P, BookMarkSize ); - {$ELSE} - FillChar(P^, BookMarkSize, 0 ); - {$ENDIF} - with TPSQLBookMark(P^) do - Position := RecordNumber+1; -end; - -procedure TNativeDataSet.GetVchkDesc(iValSeqNo: Word; var pvalDesc: VCHKDesc); -begin - pvalDesc := Fields[iValSeqNo].ValCheck; -end; - -procedure TNativeDataSet.GetCursorProps( var curProps : CURProps ); -begin - {$IFNDEF NEXTGEN} - ZeroMemory(@curProps, SizeOf(curProps)); - {$ELSE} - FillChar(curProps, SizeOf(curProps), 0 ); - {$ENDIF} - With curProps do - begin - iFields := FieldCount; - iRecSize := RecordSize; - iRecBufSize := GetWorkBufferSize; { Record size (physical record) } - iValChecks := FieldCount; - iBookMarkSize := BookMarkSize; { Bookmark size } - bBookMarkStable := False; { Stable book marks } - eOpenMode := FOMode; { ReadOnly / RW } - iSeqNums := 1; { 1: Has Seqnums; 0: Has Record# } - exltMode := xltNONE; { Translate Mode } - bUniDirectional := True; { Cursor is uni-directional } - iFilters := FFilters.Count; { Number of Filters } - if isQuery then - begin - iIndexes := 0; - iKeySize := 0; - end else - begin - iIndexes := IndexCount; - iKeySize := FKeyDesc.iKeyLen; { Key size } - end; - bSoftDeletes := False; - end; -end; - -procedure TNativeDataSet.GetFieldDescs(var pFDesc : TFLDDescList); -var - i : Integer; -begin - for i := Low(pFDesc) to High(pFDesc) do - pFDesc[i] := Fields[i+1].Description; -end; - -procedure TNativeDataSet.Execute; -begin - if FOpen then CloseTable; - FAffectedRows := 0; - FStatement := nil; - if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; - FLastOperationTime := GetTickCount; - FStatement := _PQExecute(FConnect, SQLQuery); - if FStatement <> nil then - begin - try - FConnect.CheckResult(FStatement); - MonitorHook.SQLExecute(Self, True); - except - MonitorHook.SQLExecute(Self, False); - CloseTable; - raise; - end; - FAffectedRows := StrToIntDef(String(PQcmdTuples(FStatement)), 0); - FLastOperationTime := GetTickCount - FLastOperationTime; - PQclear(FStatement); - FStatement := nil; - end else - FConnect.CheckResult; - SQLQuery := ''; -end; - -function TNativeDataset.FieldCount: Integer; -begin - if FStatement = nil then Result := 0 - else Result := PQnfields(FStatement); -end; - -function TNativeDataSet.GetRecordSize: Integer; -var - I, Size: Integer; -begin - Size := 0; - Result := 0; - if FRecSize = -1 then - begin - if FStatement = nil then exit; - - for I := 1 to FieldCount do - Inc(Size, Fields[i].NativeSize); - - Inc(Size, FieldCount); - - FRecSize := Size; - Result := Size; - end - else - Result := FRecSize; -end; - -function TNativeDataSet.FieldName(FieldNum: Integer): String; -begin - Result := ''; - if FStatement <> nil then - Result := FConnect.RawToString(PQfname(FStatement, FieldNum)); -end; - -function TNativeDataSet.FieldIndex(FieldName: String): Integer; -var - P: PAnsiDACChar; -begin - Result := -1; - if FStatement <> nil then - begin - P := FConnect.StringToRaw(FieldName); - try - Result := PQfnumber(FStatement, P); - finally - DACAnsiStrDispose(P); - end; - end; -end; - -function TNativeDataSet.FieldSize(FieldNum: Integer): Integer; -begin - Result := 0; - if (FStatement <> nil) and (PQntuples(FStatement) > 0) then - Result := PQgetlength(FStatement, GetRecNo, FieldNum); -end; - -function TNativeDataSet.FieldMaxSizeInBytes(FieldNum: Integer): Integer; -var FT: cardinal; -begin - FT := FieldType(FieldNum); - case FT of - FIELD_TYPE_BOOL, - FIELD_TYPE_INT2: Result := SizeOf(Smallint); - - FIELD_TYPE_INT4: Result := SizeOf(Integer); - - FIELD_TYPE_INT8: Result := SizeOf(Int64); - - FIELD_TYPE_DATE, - FIELD_TYPE_TIME, - FIELD_TYPE_TIMESTAMP: Result := SizeOf(TDateTime); - - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: Result := Sizeof(Double); - - FIELD_TYPE_NUMERIC: Result := SizeOf(TBcd); - -{$IFDEF DELPHI_12} - FIELD_TYPE_POINT: Result := SizeOf(TPSQLPoint); - FIELD_TYPE_CIRCLE: Result := SizeOf(TPSQLCircle); - FIELD_TYPE_BOX: Result := SizeOf(TPSQLBox); - FIELD_TYPE_LSEG: Result := SizeOf(TPSQLLSeg); - - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: Result := SizeOf(TPSQLRange); -{$ENDIF DELPHI_12} - - FIELD_TYPE_TEXT, - FIELD_TYPE_BYTEA, - FIELD_TYPE_OID: Result := SizeOf(TBlobItem); - FIELD_TYPE_UUID: Result := UUIDLEN + 1; - else - begin - case FT of - FIELD_TYPE_UNKNOWN: Result := NAMEDATALEN; - FIELD_TYPE_INET, FIELD_TYPE_CIDR: Result := INETLEN; - FIELD_TYPE_MACADDR: Result := MACADDRLEN; - FIELD_TYPE_TIMESTAMPTZ: Result := TIMESTAMPTZLEN; - FIELD_TYPE_TIMETZ: Result := TIMETZLEN; - FIELD_TYPE_NAME: Result := NAMEDATALEN; - else - Result := FieldMaxSize(FieldNum); - end; - {$IFDEF DELPHI_12} - if FConnect.IsUnicodeUsed then - Result := (Result + 1 )* SizeOf(Char) //we need two #0 bytes here 25.11.2008 - else - {$ENDIF} - Result := Result + 1; - end; - end; -end; - -function TNativeDataSet.GetFieldTypType(Index: integer): AnsiDACChar; -var fCount: integer; - fTypType: string; - fTypeOid: oid; - IsOK: boolean; -begin - fCount := FieldCount(); - if (Length(FFieldTypType) < fCount) then - FFieldTypType := StringOfChar(AnsiDACChar('u'), fCount); //unknown - if FFieldTypType[Index + 1] = 'u' then - begin - fTypeOid := FieldType(Index); - if fTypeOid < MAX_BUILTIN_TYPE_OID then - FFieldTypType[Index + 1] := 'b' //base - else - begin - fTypType := FConnect.SelectStringDirect('SELECT typtype FROM pg_catalog.pg_type WHERE oid = ' + UIntToStr(fTypeOid), IsOK, 0); - if fTypType > '' then - FFieldTypType[Index + 1] := AnsiDACChar(fTypType[1]) - else - FFieldTypType[Index + 1] := 'X'; //failed to obtain - end; - end; - Result := FFieldTypType[Index + 1]; -end; - -function TNativeDataSet.FieldMaxSize(FieldNum: Integer): Integer; -Var fMod: integer; - fTypeOid: oid; -begin - Result := 0; - if FStatement <> nil then - begin - fMod := Max(PQfmod(FStatement, FieldNum), 0); - fTypeOid := FieldType(FieldNum); - case fTypeOid of - FIELD_TYPE_BPCHAR, - FIELD_TYPE_VARCHAR: Result := (fMod - 4); - FIELD_TYPE_BIT, - FIELD_TYPE_VARBIT: Result := fMod; - - FIELD_TYPE_NUMERIC: Result := fMod; // shr 16 and 65535 + 1; //frac delimiter - else - if fTypeOid > MAX_BUILTIN_TYPE_OID then //suppose it's UDT or enum - case FieldTypTypes[FieldNum] of //we're interested in composites & enums only - 'c': ;//composite TODO - 'e': Result := NAMEDATALEN; //enum - end; - end; - if Result <= 0 then - Result := FieldMinSize(FieldNum); - end; -end; - -function TNativeDataSet.FieldMinSize(FieldNum: Integer): Integer; -var - I, H: Integer; -begin - if dsoUDTAsMaxString in FOptions then - begin - Result := MAX_CHAR_LEN; - Exit; - end - else - Result := 0; - if not Assigned(FFieldMinSizes) or - (High(FFieldMinSizes) < FieldNum) or - (FFieldMinSizes[FieldNum] = -1) - then - begin - if Assigned(FFieldMinSizes) then - H := High(FFieldMinSizes) + 1 - else - H := 0; - SetLength(FFieldMinSizes, FieldNum + 1); - for i := H to High(FFieldMinSizes) - 1 do - FFieldMinSizes[i] := -1; - if FStatement <> nil then - for I := 0 to PQntuples(FStatement) - 1 do - if PQgetlength(FStatement, I, FieldNum) > Result then - Result := PQgetlength(FStatement, I, FieldNum); - if Result = 0 then - Result := MAX_CHAR_LEN; //there is no field of length 0 - FFieldMinSizes[FieldNum] := Result; - end - else - Result := FFieldMinSizes[FieldNum]; -end; - -function TNativeDataSet.FieldType(FieldNum: Integer): cardinal; -begin - Result := InvalidOid; - if FStatement <> nil then - Result := PQftype(FStatement, FieldNum); - case Result of - FIELD_TYPE_OID: if dsoOIDAsInt in FOptions then - Result := FIELD_TYPE_INT8; - FIELD_TYPE_BYTEA: if dsoByteaAsEscString in FOptions then - Result := FIELD_TYPE_TEXT; - FIELD_TYPE_NUMERIC: -{$IFDEF DELPHI_12} - if dsoNumericAsFloat in FOptions then -{$ENDIF} - Result := FIELD_TYPE_FLOAT8; - FIELD_TYPE_OIDVECTOR: Result := FIELD_TYPE_VARCHAR; - FIELD_TYPE_CID, - FIELD_TYPE_XID, - FIELD_TYPE_TID: Result := FIELD_TYPE_INT4; - else - if (Result = FIELD_TYPE_VARCHAR) AND - ((PQfmod(FStatement, FieldNum) < 0) or (PQfmod(FStatement, FieldNum) > MAX_CHAR_LEN)) - or - (Result = FIELD_TYPE_XML) then - Result := FIELD_TYPE_TEXT; //added to deal with varchar without length specifier - end; -end; - -function TNativeDataSet.FieldTypMod(FieldNum: Integer): Integer; -begin - Result := -1; - if Assigned(FStatement) then - Result := PQfmod(FStatement, FieldNum); -end; - -function TNativeDataSet.FetchRecords(const NumberOfRecs: integer = 1): integer; -var - LocResult: PPGResult; - i: integer; - fval: PAnsiDACChar; - flen: Integer; - CurrentRecNum: Integer; -const - NULL_LEN: integer = -1; -begin - Result := 0; - repeat - LocResult := PQgetResult(FConnect.Handle); - FFetched := not Assigned(LocResult); - case PQresultStatus(LocResult) of - PGRES_SINGLE_TUPLE: - begin - CurrentRecNum := PQntuples(FStatement); - for i := 0 to PQnfields(LocResult) - 1 do - begin - if PQgetisnull(LocResult, 0, i) = 1 then - flen := NULL_LEN - else - flen := PQgetlength(LocResult, 0, i); - fval := PQgetvalue(LocResult, 0, i); - if PQsetvalue(FStatement, CurrentRecNum, i, fval, flen) = 0 then - raise EPSQLException.CreateFmt('Cannot consume row on demand. Operation for field "%s" failed', [PQfname(LocResult, i)]); - end; - PQClear(LocResult); - inc(Result); - end; - PGRES_TUPLES_OK: - begin - LocResult := PQgetResult(FConnect.Handle); - FFetched := not Assigned(LocResult); - end; - else - Exit; //no rows for fetching, command returning no data executed? - end; - until (Result = NumberOfRecs) or FFetched; -end; - -function TNativeDataSet.Field(FieldNum: Integer): string; -begin - Result := ''; - if FStatement = nil then Exit; - Result := FConnect.RawToString(PQgetvalue(FStatement,GetRecNo,FieldNum)); - if Fieldtype(FieldNum) = FIELD_TYPE_BPCHAR then - Result := TrimRight(Result); -end; - -function TNativeDataSet.FieldIsNull(FieldNum: Integer): Boolean; -begin - Result := true; - if FStatement <> nil then - Result := PQgetisnull(FStatement,GetRecNo,FieldNum) <> 0; -end; - -function TNativeDataSet.FieldBuffer(FieldNum: Integer): PAnsiDACChar; -begin - Result := nil; - if (FStatement = nil) or (PQgetisnull(FStatement, GetRecNo, FieldNum) <> 0) then Exit; - Result := PQgetvalue(FStatement, GetRecNo, FieldNum); -end; - -procedure TNativeDataSet.GetNativeDesc(FieldNo : Integer; var P : FldDesc; var P1 : VCHKDesc; Var LocType, LocSize : Integer; var LocArray : Boolean); -var - Fld : TPGFIELD_INFO; -begin - CheckParam(not (FieldNo <= FieldCount), DBIERR_INVALIDRECSTRUCT); - FLD := FieldInfo[FieldNo-1]; - ConverPSQLtoDelphiFieldInfo(FLD, FieldNo, FieldOffset(FieldNo), P, P1, LocArray); - LocType := FieldType(FieldNo-1); - case Loctype of - FIELD_TYPE_BYTEA, - FIELD_TYPE_TEXT, - FIELD_TYPE_OID: LocSize := SizeOf(TBlobItem); - else - LocSize := FieldMaxSizeInBytes(FieldNo-1); - end; -end; - -function TNativeDataSet.GetFieldInfo(Index : Integer) : TPGFIELD_INFO; -var - Item : TPSQLNative; - I : Integer; - - procedure FillDefsAndNotNulls(); - Var inS: String; - i, j, fPos: integer; - tabOID: cardinal; - RES: PPGresult; - sql: String; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} - const - tS = ' c.oid = %d AND a.attnum = %d '; - - begin - if IsQuery and (FOMode = dbiReadOnly) then Exit; - sql := 'SELECT a.attnum, '#13#10 + - ' c.oid, '#13#10 + - 'CASE WHEN a.attnotnull OR t.typtype = ''d''::"char" AND t.typnotnull '#13#10 + - ' THEN FALSE ELSE TRUE END AS is_nullable, '#13#10+ - 'pg_get_expr(ad.adbin, ad.adrelid) '#13#10 + - 'FROM pg_attribute a LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum, '#13#10 + - 'pg_class c, pg_type t '#13#10 + - 'WHERE a.atttypid = t.oid AND a.attrelid = c.oid '#13#10 + - 'AND a.attnum > 0 AND not a.attisdropped '#13#10 + - 'AND (%s) '#13#10 + - 'ORDER BY a.attnum'; - - if not isQuery then - inS := ' c.oid = ' + IntToStr(FieldTable(0)) - else - for i:=0 to FieldCount-1 do - begin - tabOID := FieldTable(I); - fPos := FieldPosInTable(I); - if (tabOID > InvalidOid) and (fPos > -1) then - if inS > '' then - inS := inS + 'OR' + Format(ts,[tabOID,fPos]) - else - inS := Format(ts,[tabOID,fPos]); - end; - if inS > '' then - begin - sql := Format(sql, [inS]); - Res := PQExec(FConnect.Handle, {$IFNDEF NEXTGEN} - PAnsiChar(AnsiString(sql)) - {$ELSE} - M.AsAnsi(sql).ToPointer - {$ENDIF}); - if Assigned(RES) then - try - FConnect.CheckResult; - for i := 0 to PQntuples(RES) - 1 do - for j := 0 to FieldCount - 1 do - if (IntToStr(FieldTable(j)) = FConnect.RawToString(PQGetValue(Res, i, 1))) and - (IntToStr(FieldPosInTable(j)) = FConnect.RawToString(PQGetValue(Res, i, 0))) then - with TPSQLNative(FNativeDescs.Items[j]) do - begin - NativeNotNull := FConnect.RawToString(PQgetvalue(RES, i, 2)) = 'f'; - NativeDefault := FConnect.RawToString(PQgetvalue(RES, i, 3)); - end; - finally - PQclear(RES); - end; - end; - end; - -begin - if FNativeDescs.Count = 0 then - begin - for I := 0 to FieldCount - 1 do - FNativeDescs.SetNative(I, FieldName(I), FieldType(I), FieldMaxSizeInBytes(I), FieldMaxSize(I), FieldTypMod(I)); - FillDefsAndNotNulls(); - end; - Item := TPSQLNative(FNativeDescs.Items[Index]); - if Item <> nil then - Result := Item.FDesc; -end; - -procedure TNativeDataSet.InitFieldDescs; -var - i : Integer; - FldInfo : FLDDesc; - ValCheck : VCHKDesc; - LocalType, LocalSize : Integer; - RecSize, NullOffset: Integer; - LocArray : Boolean; -begin - Fields.Clear; - for i := 1 to FieldCount() do - begin - GetNativeDesc(i, FldInfo, ValCheck, LocalType, LocalSize, LocArray); - Fields.AddField(FldInfo, ValCheck, i, LocalType, LocalSize, LocArray); - Finalize(FldInfo); //without this calls we have memory leak - Finalize(ValCheck); - end; - - RecSize := RecordSize; - NullOffset := RecSize + 1; - for i := 1 to Fields.Count do - begin - Fields[i].NullOffset := NullOffset; - Inc(NullOffset, SizeOf(TFieldStatus)); - end; -end; - -function TNativeDataSet.GetBufferSize : integer; -begin - if FFieldDescs.Count = 0 then InitFieldDescs; - Result := RecordSize; -end; - -function TNativeDataSet.GetWorkBufferSize : integer; -begin - Result := GetBufferSize; - Inc(Result, FFieldDescs.Count * SizeOf(TFieldStatus) + 1); - FBookOfs := Result; - if FBookOfs > 0 then Inc(Result, BookMarkSize); -end; - -procedure TNativeDataSet.GetProp(iProp: integer;PropValue: Pointer;iMaxLen: integer; var iLen: integer); -begin - iLen := 0; - Case TPropRec( iProp ).Prop of - Word( curMAXPROPS ): begin - iLen := SizeOf(Word); - Word(PropValue^) := maxCurProps; - end; - Word( curXLTMODE ): begin - iLen := SizeOf(xltMODE); - xltMODE( PropValue^ ) := xltNONE; - end; - Word(curMAXFIELDID): begin - iLen := iMaxLen; - Integer( PropValue^ ) := FFieldDescs.Count; - end; - Word(stmtROWCOUNT): begin - iLen := SizeOf(Integer); - Integer(PropValue^) := FAffectedRows; - end; - Word(curAUTOREFETCH):begin - iLen := SizeOf(Boolean); - Boolean(PropValue^) := FReFetch; - end; - end; -end; - -procedure TNativeDataSet.SetProp(iProp: integer;PropValue: Longint); -begin - Case TPropRec( iProp ).Prop of - Word(curMAKECRACK): RecordState := tsEmpty; - Word(stmtLIVENESS): begin - if PropValue = 1 then - FOMode := dbiReadWrite else - FOMode := dbiREADONLY; - end; - Word(curAUTOREFETCH): FReFetch := PropValue > 0; - end; -end; - -procedure TNativeDataSet.SetToBegin; -begin - RecordState := tsFirst; -end; - -procedure TNativeDataSet.SetToEnd; -begin - RecordState := tsLast; -end; - - -procedure TNativeDataSet.InternalReadBuffer; -var - i, size: Integer; - null: boolean; //temp var used for work with dsoEmptyCharAsNull - MaxSize, tMS : Integer; - T: TPSQLField; - origBuffer: Pointer; - FldValue : String; - Data : pointer; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if Assigned(FCurrentBuffer) then - begin - MaxSize := 0; - for i := 0 to FieldCount - 1 do - case FieldType(I) of - FIELD_TYPE_OID, FIELD_TYPE_TEXT, FIELD_TYPE_BYTEA: //ignore - else - begin - tMS := FieldMaxSizeInBytes(I); - if tMS > MaxSize then MaxSize := tMS; - end; - end; - GetMem(Data, MaxSize + 1); - origBuffer := FCurrentBuffer; - for i := 0 to FieldCount - 1 do - begin - T := Fields[i+1]; - T.Buffer := origBuffer; - T.FieldChanged := FALSE; - null := FieldIsNull(I); - T.FieldNull := null; - size := T.NativeSize; //FieldLength - if null then - {$IFNDEF NEXTGEN} - ZeroMemory(FCurrentBuffer,size) - {$ELSE} - FillChar(FCurrentBuffer^, size, 0) - {$ENDIF} - else - begin - if (T.NativeType <> FIELD_TYPE_OID) and - (T.NativeType <> FIELD_TYPE_TEXT) and - (T.NativeType <> FIELD_TYPE_BYTEA) - then - FldValue := FConnect.RawToString(FieldBuffer(i)); - case T.NativeType of - FIELD_TYPE_INT2: SmallInt(Data^) := SmallInt(StrToint(FldValue)); - FIELD_TYPE_BOOL: if FldValue = 't' then SmallInt(Data^) := 1 else SmallInt(Data^) := 0; - FIELD_TYPE_INT4: LongInt(Data^) := LongInt(StrToint(FldValue)); - FIELD_TYPE_INT8: Int64(Data^) := StrToInt64(FldValue); - FIELD_TYPE_DATE: TDateTime(Data^) := SQLDateToDateTime(FldValue, False); - FIELD_TYPE_TIME: TDateTime(Data^) := SQLDateToDateTime(FldValue, True); - FIELD_TYPE_TIMESTAMP: TDateTime(Data^) := SQLTimeStampToDateTime(FldValue); -{$IFDEF UNDER_DELPHI_12} - FIELD_TYPE_NUMERIC, -{$ENDIF} - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: Double(Data^) := StrToSQLFloat(FldValue); -{$IFDEF DELPHI_12} - FIELD_TYPE_NUMERIC: TBcd(Data^) := StrToBcd(FldValue, PSQL_FS); - FIELD_TYPE_POINT: TPSQLPoint(Data^) := SQLPointToPoint(FldValue); - FIELD_TYPE_CIRCLE: TPSQLCircle(Data^) := SQLCircleToCircle(FldValue); - FIELD_TYPE_BOX: TPSQLBox(Data^) := SQLBoxToBox(FldValue); - FIELD_TYPE_LSEG: TPSQLLSeg(Data^) := SQLLSegToLSeg(FldValue); - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: TPSQLRange(Data^) := SQLRangeToRange(FldValue, T.NativeType); -{$ENDIF DELPHI_12} - FIELD_TYPE_OID, - FIELD_TYPE_TEXT, - FIELD_TYPE_BYTEA: begin - size := SizeOf(TBlobItem); - {$IFNDEF NEXTGEN} - ZeroMemory(FCurrentBuffer, Size); - {$ELSE} - FillChar(FCurrentBuffer^, Size, 0); - {$ENDIF} - Inc(PAnsiDACChar(FCurrentBuffer)); //Null byte allocate - Inc(PAnsiDACChar(FCurrentBuffer), Size); //Pointer allocate - Continue; - end; - {$IFNDEF NEXTGEN} - FIELD_TYPE_UUID: {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrCopy(PAnsiChar(Data), PAnsiChar(BadGuidToGuid(AnsiString(FldValue)))); - {$ELSE} - FIELD_TYPE_UUID: DACStrCopy(PAnsiDACChar(Data), M.AsAnsi(BadGuidToGuid(FldValue)).ToPointer); - {$ENDIF} - else - if dsoTrimCharFields in FOptions then - FldValue := TrimRight(FldValue); - if FConnect.IsUnicodeUsed then - {$IFDEF DELPHI_12} - StrCopy(PWideChar(Data), PWideChar(FldValue)) - {$ELSE} - StrCopy(PAnsiChar(Data), PAnsiChar(FldValue)) - {$ENDIF} - else - {$IFNDEF NEXTGEN} - {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrCopy(PAnsiChar(Data), PAnsiChar(AnsiString(FldValue))); - {$ELSE} - DACStrCopy(PAnsiDACChar(Data), M.AsAnsi(FldValue).ToPointer); - {$ENDIF} - end; - Move(Data^, (PAnsiDACChar(FCurrentBuffer) + 1)^, Size); - PAnsiDACChar(FCurrentBuffer)^ := #1; {null indicator 1=Data 0=null} - end; - Inc(PAnsiDACChar(FCurrentBuffer), Size + 1); {plus 1 for null byte} - end; - FreeMem(Data, MaxSize + 1); - FCurrentBuffer := nil; - end; -end; - -procedure TNativeDataSet.ReadBlock(var iRecords : integer; pBuf : Pointer); -var - M : MemPtr; - i : integer; - Limit : longint; -begin - Limit := iRecords; - iRecords := 0; - CheckParam(pBuf = nil, DBIERR_INVALIDPARAM); - M := pBuf; - i := 0; - repeat - GetNextRecord(dbiNOLOCK, @M^[ i ], NIL); - Inc(iRecords); - if iRecords >= Limit then - Break - else - Inc(i, GetWorkBufferSize); - until False; -end; - -procedure TNativeDataSet.ForceReread; -var - P : TPSQLBookMark; -begin - GetBookMark(@P); - FReRead := TRUE; - ReOpenTable; - if RecordCount > 0 then - SetToBookmark(@P); -end; - -procedure TNativeDataSet.CompareBookMarks( pBookMark1, pBookMark2 : Pointer; var CmpBkmkResult : CmpBkmkRslt ); - - function cmp2Values(val1, val2: LongInt): CmpBkmkRslt; - begin - if val1 = val2 then result := CMPEql else if val1 < val2 then result := CMPLess else result := CMPGtr; - end; - -begin - CheckParam(pBookMark1 = nil,DBIERR_INVALIDPARAM); - CheckParam(pBookMark2 = nil,DBIERR_INVALIDPARAM); - if (TPSQLBookMark(pBookMark1^).Position <> 0) then - CmpBkMkResult := cmp2Values( TPSQLBookMark(pBookMark1^).Position, TPSQLBookMark(pBookMark2^).Position) else - CmpBkMkResult := CMPGtr; -end; - -procedure TNativeDataSet.InitRecord(PRecord : Pointer); -begin - if PRecord = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDPARAM); - {$IFNDEF NEXTGEN} - ZeroMemory(PRecord, GetWorkBufferSize); - {$ELSE} - FillChar(PRecord^, GetWorkBufferSize, 0); - {$ENDIF} - FFieldDescs.SetFields(PRecord); - CurrentBuffer := PRecord; -end; - - -procedure TNativeDataSet.GetKeys(Unique: Boolean; var FieldList: TFieldArray; var FieldCount: Integer); -var - I, N: Integer; - Item : TPSQLIndex; - Fld : TPSQLField; -begin - N := -1; - FieldCount := 0; - //Search for PrimaryKey - for I := 1 to FindexDescs.Count do - begin - Item := FIndexDescs.mIndex[I]; - if Item.Primary then - begin - N := I; - Break; - end; - end; - if N = -1 then - //Primary key not found. - //Search for Unique Key - for I := 1 to FindexDescs.Count do - begin - Item := FIndexDescs.mIndex[I]; - if Item.Unique then - begin - N := I; - break; - end; - end; - if N >= 0 then - begin - Item := FindexDescs.mIndex[N]; - for I := 0 to Item.FldsInKey-1 do - begin - FieldList[FieldCount] := Item.FDesc.aiKeyFld[I]; - Inc(FieldCount); - end; - end - else - if not Unique then - begin - for I := 1 to FFieldDescs.Count do - begin - Fld := FFieldDescs.Field[I]; - if not(Fld.FieldType in [fldBlob]) then - begin - if Fld.FDesc.bCalcField then continue; - FieldList[FieldCount] := I; - Inc(FieldCount); - end; - end; - end; -end; - -function TNativeDataSet.GetLOUnlinkSQL(ObjOID: string): string; -const - _LoMng: string = #13#10'SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = %) THEN lo_unlink(%) END;'; -begin - Result := StringReplace(_LoMng, '%', ObjOID, [rfReplaceAll]); -end; - -function TNativeDataSet.GetDeleteSQL(Table: string; PRecord: Pointer): string; -var - AFieldCount, I: Integer; - FieldList : TFieldArray; - Fld : TPSQLField; -begin - Result := ''; - GetKeys(False, FieldList, AFieldCount); - for I := 0 to AFieldCount-1 do - begin - Fld := FFieldDescs.Field[FieldList[I]]; - Fld.Buffer:= PRecord; - if Result <> '' then Result := Result + ' AND '; - Result := Result + AnsiQuotedStr(Fld.FieldName, '"'); - if Fld.FieldNull then - Result := Result + ' IS NULL' - else - Result := Result + '=' + Fld.FieldValueAsStr; - end; - if Result = '' then Exit; - Result := 'DELETE FROM ' + Table + ' WHERE ' + Result; - - if not (dsoManageLOFields in FOptions) then Exit; - for I := 1 to FFieldDescs.Count do - begin - Fld := FFieldDescs.Field[I]; - if (Fld.NativeBLOBType = nbtOID) and not FieldIsNull(I-1) then - Result := GetLOUnlinkSQL(Field(I-1)) + #13#10 + Result; - end; -end; - -procedure TNativeDataSet.FreeBlobStreams(PRecord: Pointer); -var - I : Integer; -begin - for I := 1 to FFieldDescs.Count do - if FFieldDescs.Field[I].FieldType = fldBLOB then - FreeBlob(PRecord,i); -end; - -function TNativeDataSet.GetInsertSQL(Table: string; PRecord: Pointer; ReturnUpdated: boolean = False): string; -var - I : Integer; - Fld : TPSQLField; - SFields : String; - Values : String; - - function GetRETURNING: string; - var I: integer; - begin - Result := ''; - if not ReturnUpdated then Exit; - if FieldCount > 0 then Result := ' RETURNING ' + AnsiQuotedStr(FieldName(0), '"'); - for I := 1 to FieldCount-1 do - Result := Result + ', ' + AnsiQuotedStr(FieldName(I), '"'); - end; - -begin - Result := ''; - SFields := ''; - for I := 1 to FFieldDescs.Count do - begin - Fld := FFieldDescs.Field[I]; - Fld.Buffer:= PRecord; - if (Fld.FieldNull) and (not Fld.FValCheck.bHasDefVal) then continue; - SFields := SFields + AnsiQuotedStr(Fld.FieldName, '"') + ', '; - if (Fld.FieldNull) and (Fld.FValCheck.bHasDefVal) then - Values := Values + 'DEFAULT, ' - else - Values := Values + Fld.FieldValueAsStr + ', '; - end; - Delete(SFields, Length(SFields)-1, 2); - Delete(Values, Length(Values)-1, 2); - if (SFields <> '') and (Values <> '') then - Result := Format('INSERT INTO %s (%s) VALUES (%s)', [Table, SFields, Values]) + GetReturning(); -end; - -function TNativeDataSet.GetUpdateSQL(Table: string; OldRecord,PRecord: Pointer; ReturnUpdated: boolean = False): String; -var - I: Integer; - Fld: TPSQLField; - FldName, Values: string; - - function GetWHERE(P : Pointer) : String; - var - I, AFieldCount: Integer; - FieldList : TFieldArray; - Fld : TPSQLField; - FldName: string; - begin - Result := ''; - GetKeys(False, FieldList, AFieldCount); - for I := 0 to AFieldCount-1 do - begin - Fld := FFieldDescs.Field[FieldList[I]]; - Fld.Buffer:= P; - FldName := AnsiQuotedStr(Fld.FieldName, '"'); - if Result <> '' then Result := Result + ' AND '; - if Fld.FieldNull then - Result := Result + FldName + ' IS NULL' - else - Result := Result + FldName + '=' + Fld.FieldValueAsStr; - end; - Result := ' WHERE ' + Result; - end; - - function GetRETURNING: string; - var I: integer; - begin - Result := ''; - if not ReturnUpdated then Exit; - if FieldCount > 0 then Result := ' RETURNING ' + AnsiQuotedStr(FieldName(0), '"'); - for I := 1 to FieldCount-1 do - Result := Result + ', ' + AnsiQuotedStr(FieldName(I), '"'); - end; - -begin - Result := ''; - for I := 1 to FFieldDescs.Count do - begin - Fld := FFieldDescs.Field[I]; - Fld.Buffer:= PRecord; - if not Fld.FieldChanged then Continue; - if (dsoManageLOFields in FOptions) and (Fld.NativeBLOBType = nbtOID) and not FieldIsNull(I-1) then - Result := Result + GetLOUnlinkSQL(Field(I-1)); - FldName := AnsiQuotedStr(Fld.FieldName, '"'); - if Fld.FieldNull then - Values := Values + FldName + '=NULL, ' - else - Values := Values + FldName + '=' + Fld.FieldValueAsStr + ', '; - end; - Delete(VALUES,Length(Values)-1,2); - if VALUES > '' then - Result := Format('UPDATE %s SET %s', [Table, Values]) + GetWhere(OldRecord) + GetRETURNING() + ';' + Result; //LOUnlinkSql at the end -end; - -procedure TNativeDataSet.AppendRecord (PRecord : Pointer); -begin - InsertRecord(dbiNOLOCK, PRecord); -end; - -procedure TNativeDataSet.InsertRecord( eLock : DBILockType; PRecord : Pointer ); -var - SQL : String; - OldQueryFlag: boolean; - KN, i: integer; - AStatement, ATempCopyStmt: PPGResult; - fval, fname: PAnsiDACChar; - flen: integer; - CurrentRecNum: integer; -begin - AStatement := nil; - - KN := -1; - if FOMode = dbiREADONLY then - raise EPSQLException.CreateBDE(DBIERR_TABLEREADONLY); - CheckUniqueKey(KN); - - SQL := GetInsertSQL(TableName, PRecord, dsoRefreshModifiedRecordOnly in FOptions); - try - AStatement := _PQExecute(FConnect, SQL); - if Assigned(AStatement) then - try - FConnect.CheckResult(AStatement); - MonitorHook.SQLExecute(Self, True); - FAffectedRows := StrToIntDef(string(PQcmdTuples(AStatement)), 0); - CurrentRecNum := PQntuples(FStatement); - if (FAffectedRows > 0) and (dsoRefreshModifiedRecordOnly in Options) then - begin - ATempCopyStmt := PQcopyResult(FStatement, PG_COPYRES_TUPLES); //hack because libpq have some bugs. must be eliminated further - if not Assigned(ATempCopyStmt) then - raise EPSQLException.CreateMsg(FConnect, 'Refresh for inserted fiels failed, cannot copy results'); - for i := 0 to PQnfields(AStatement) - 1 do - begin - fname := PQfname(AStatement, i); - if PQgetisnull(AStatement, 0, i) = 1 then - begin - fval := nil; - flen := -1; - end else - begin - fval := PQgetvalue(AStatement, 0, i); - flen := PQgetlength(AStatement, 0, i); - end; - if PQsetvalue(ATempCopyStmt, CurrentRecNum, i, fval, flen) = 0 then - raise EPSQLException.CreateFmt('Refresh for inserted fiels "%s" failed', [fname]); - end; - PQclear(FStatement); - FStatement := ATempCopyStmt; - RecordState := tsPos; - SettoSeqNo(RecordCount); //tuple added to the end - FReFetch := False; - end; - except - MonitorHook.SQLExecute(Self, False); - raise; - end - else - FConnect.CheckResult; - finally - PQclear(AStatement); - end; - - FreeBlobStreams(PRecord); - InternalBuffer := nil; - if (FAffectedRows > 0) and not (dsoRefreshModifiedRecordOnly in Options) then - if not FReFetch then - begin - OldQueryFlag := IsQuery; - ReOpenTable; - IsQuery := OldQueryFlag; - RecordState := tsPos; - try - if not SetRowPosition(KN, GetLastInsertID(KN), PRecord) then - SettoSeqNo(RecordCount); - except - end; - end; - FIsLocked := FALSE; -end; - -procedure TNativeDataSet.ModifyRecord(OldRecord,PRecord : Pointer; bFreeLock : Boolean; ARecNo : Longint); -var - SQL : String; - OldQueryFlag: boolean; - KN : Integer; - i: integer; - CurrentRecNum: LongInt; - AStatement: PPGResult; - fval, fname: PAnsiDACChar; - flen: integer; -begin - KN := -1; - AStatement := nil; - CurrentRecNum := RecNo; - if FOMode = dbiREADONLY then - raise EPSQLException.CreateBDE(DBIERR_TABLEREADONLY); - CheckUniqueKey(KN); - try - SQL := GetUpdateSQL(TableName, OldRecord, PRecord, dsoRefreshModifiedRecordOnly in FOptions); - if SQL <> '' then - AStatement := _PQExecute(FConnect, SQL); - if Assigned(AStatement) then - try - FConnect.CheckResult(AStatement); - MonitorHook.SQLExecute(Self, True); - FAffectedRows := StrToIntDef(string(PQcmdTuples(AStatement)), 0); - if (FAffectedRows > 0) and (dsoRefreshModifiedRecordOnly in Options) then - for i := 0 to PQnfields(AStatement) - 1 do - begin - fname := PQfname(AStatement, i); - if PQgetisnull(AStatement, 0, i) = 1 then - begin - fval := nil; - flen := -1; - end else - begin - fval := PQgetvalue(AStatement, 0, i); - flen := PQgetlength(AStatement, 0, i); - end; - if PQsetvalue(FStatement, CurrentRecNum, i, fval, flen) = 0 then - raise EPSQLException.CreateFmt('Refresh for modifed fiels "%s" failed', [fname]); - end; - FReFetch := False; - RecordState := tsPos; - except - MonitorHook.SQLExecute(Self, False); - raise; - end - else - FConnect.CheckResult; - finally - PQclear(AStatement); - end; - - FreeBlobStreams(OldRecord); - FreeBlobStreams(PRecord); - InternalBuffer := nil; - if bFreeLock then LockRecord(dbiNOLOCK); - - if (FAffectedRows > 0) and not (dsoRefreshModifiedRecordOnly in Options) then - if not FReFetch then - begin - OldQueryFlag := IsQuery; - ReOpenTable; - IsQuery := OldQueryFlag; - RecordState := tsPos; - try - if not SetRowPosition(KN, 0, PRecord) then - SettoSeqNo(CurrentRecNum + 1); - except - end; - end; - FIsLocked := FALSE; -end; - -procedure TNativeDataSet.DeleteRecord(PRecord : Pointer); -var - SQL : String; - CurrentRecNum: LongInt; - ATempCopyStmt: PPGResult; - fval: PAnsiDACChar; - flen, i, j: integer; -begin - if FOMode = dbiREADONLY then - raise EPSQLException.CreateBDE(DBIERR_TABLEREADONLY); - - InternalBuffer := PRecord; - SQL := GetDeleteSQL(TableName, PRecord); - if Sql <> '' then - begin - FConnect.QExecDirect(SQL, nil, FAffectedRows); - RecordState := tsEmpty; - end; - - FreeBlobStreams(PRecord); - InternalBuffer := nil; - - if (FAffectedRows > 0) and (dsoRefreshModifiedRecordOnly in Options) then - begin - ATempCopyStmt := PQcopyResult(FStatement, PG_COPYRES_ATTRS); //hack because libpq have some bugs. must be eliminated further - if not Assigned(ATempCopyStmt) then - raise EPSQLException.CreateMsg(FConnect, 'Refresh for deleted fiels failed, cannot copy results'); - j := 0; - for CurrentRecNum := 0 to RecordCount - 1 do - begin - if CurrentRecNum = RecNo then - Continue; // exclude row from new set and check bounds - for i := 0 to PQnfields(FStatement) - 1 do - begin - if PQgetisnull(FStatement, CurrentRecNum, i) = 1 then - begin - fval := nil; - flen := -1; - end - else - begin - fval := PQgetvalue(FStatement, CurrentRecNum, i); - flen := PQgetlength(FStatement, CurrentRecNum, i); - end; - if PQsetvalue(ATempCopyStmt, J, i, fval, flen) = 0 then - raise EPSQLException.CreateFmt('Refresh for deleted fiels failed', []); - end; - INC(J); - end; - PQclear(FStatement); - FStatement := ATempCopyStmt; - RecordState := tsPos; - try - SettoSeqNo(Min(RecNo + 1, RecordCount)); - except - // - end; - end; - - if (FAffectedRows > 0) and not (dsoRefreshModifiedRecordOnly in Options) then - if not FReFetch then - begin - CurrentRecNum := RecNo; - ReOpenTable; - RecordState := tsPos; - if CurrentRecNum >= RecordCount then - CurrentRecNum := RecordCount; - try - SettoSeqNo(CurrentRecNum); - except - end; - end; - FIsLocked := FALSE; -end; - -function TNativeDataSet.GetTableName : string; -var IsOK: boolean; - s: string; -begin - Result := FTablename; - if (Length(Result) = 0) and (FOMode <> dbiREADONLY) then - begin - s := Format('SELECT %u::regclass',[FieldTable(0)]); - Result := FConnect.SelectStringDirect(PChar(s),IsOK,0); - if IsOK then - FTableName := Result; - end; -end; - -procedure TNativeDataSet.SetTableName(Name : string); -begin - FTableName := Name; -end; - -function TNativeDataSet.GetSQLClause: string; -begin - Result := StandartClause.Text + RangeClause.Text + OrderClause.Text + LimitClause.Text -end; - -function TNativeDataSet.GetIndexCount : Integer; -var - i: Integer; - ATableOID: cardinal; - aPrim,aUniq,aSort : Boolean; - Buffer : String; - J : Integer; - LastIdx: integer; - sSQLQuery: string; - RES: PPGresult; - IdxName: string; -begin - Result := 0; - if not FIndexDescs.Updated then - begin - if isQuery and (FOMode = dbiReadOnly) or (FieldCount <= 0) - then Exit; //multitable or non-Select SQL query - - ATableOID := FieldTable(0); - sSqlQuery := 'SELECT t1.relname AS name,'#13#10+ - ' i.indisunique as "unique",'#13#10+ - ' i.indkey as fields,'#13#10+ - ' i.indisprimary'#13#10+ - ' FROM "pg_index" as i, "pg_class" as t1, "pg_class" as t2'#13#10+ - ' WHERE i.indexrelid = t1.oid'#13#10+ - ' AND i.indrelid = t2.oid'#13#10+ - ' AND t2.oid = %u'#13#10+ - ' AND i.indexprs IS NULL'#13#10; - sSQLQuery := Format(sSQLQuery, [ATableOID] ); - - Res := _PQExecute(FConnect, sSQLQuery); - if Assigned(RES) then - try - FConnect.CheckResult; - for i := 0 to PQntuples(RES) - 1 do - begin - aUniq := PQgetvalue(Res,i,1) = 't'; - aPrim := PQgetvalue(Res,i,3) = 't'; - aSort := False; - Buffer := FConnect.RawToString(PQgetvalue(Res,i,2)); - for J :=1 to Length(Buffer) do - if Buffer[J] = ' ' then Buffer[J] := ','; - IdxName := FConnect.RawToString(PQgetvalue(Res,i,0)); - LastIdx := FIndexDescs.SetIndex(IdxName, Buffer, aPrim, aUniq, aSort); - if LastIdx > 0 then - if aPrim and (FPrimaryKeyNumber = 0) then - FPrimaryKeyNumber := LastIdx; - end; - FIndexDescs.Updated := True; - finally - PQclear(RES); - end; - end; - Result := FIndexDescs.Count; -end; - -procedure TNativeDataSet.OpenBlob(PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode); -var - AField : TPSQLField; - Mode : Integer; -begin - if eOpenMode = dbiREADONLY then Mode := INV_READ else Mode := INV_WRITE; - AField := Fields[FieldNo]; - CheckParam(AField.FieldType <> fldBLOB,DBIERR_NOTABLOB); - if AField.NativeBLOBType = nbtOID then //make sure we have deal with lo_xxx - if FieldBuffer(FieldNo-1) <> nil then - begin - FBlobHandle := StrToUInt(Self.Field(FieldNo-1)); - if FBlobHandle <> InvalidOID then - begin - FConnect.BeginBLOBTran; - FLocalBHandle := lo_open(Fconnect.Handle, FBlobHandle, Mode); - if FLocalBHandle >= 0 then - FBlobOpen := True - else - FConnect.RollbackBLOBTran; //17.08.2009 - end; - end; -end; - -procedure TNativeDataSet.CloseBlob(FieldNo: Word); -var - AField : TPSQLField; -begin - AField := Fields[FieldNo]; - CheckParam(AField.FieldType <> fldBLOB, DBIERR_NOTABLOB); - if FBlobOpen and (AField.NativeBLOBType = nbtOID) and (FLocalBHandle >= 0) then - begin - lo_close(FConnect.Handle, FLocalBHandle); - FConnect.CommitBLOBTran; - FBlobHandle := InvalidOid; - FBlobOpen := False; - end; -end; - -procedure TNativeDataSet.FreeBlob(PRecord: Pointer; FieldNo: Word); -var - AField : TPSQLField; - Buff : Pointer; -begin - AField := Fields[FieldNo]; - CheckParam(AField.FieldType <> fldBLOB, DBIERR_NOTABLOB); - AField.Buffer := PRecord; - Buff := AField.FieldValue; - if PAnsiDACChar(Buff)^ = #1 then //blob stream was created - begin - PAnsiDACChar(Buff)^ := #0; - Inc(PAnsiDACChar(Buff)); - FreeAndNil(TBlobItem(Buff^).Blob); - end; - CloseBlob(FieldNo); -end; - -procedure TNativeDataSet.GetBlobSize(PRecord : Pointer; FieldNo : Word; var iSize : integer); -Var - AField : TPSQLField; - - function BlobSize(columnNumber: Integer; buff :Pointer): LongInt; - begin - Result := 0; - if AField.FieldSubType = fldstMemo then - begin - if Assigned(FieldBuffer(ColumnNumber - 1)) then - if FConnect.IsUnicodeUsed then - Result := Length(FConnect.RawToString(FieldBuffer(ColumnNumber-1))) * SizeOf(Char) - else - Result := FieldSize(ColumnNumber-1); - end - else - if FBlobOpen then - begin - Result := lo_lseek(FConnect.Handle, FLocalBHandle, 0, PG_SEEK_END); - lo_lseek(FConnect.Handle, FLocalBHandle, 0, PG_SEEK_SET); - end; - end; - - function ByteaSize(ColumnNumber: Integer):integer; - var P: PAnsiDACChar; - i, Len: integer; - begin - Result := 0; - if FieldBuffer(ColumnNumber-1) = nil then Exit; - P := FieldBuffer(ColumnNumber-1); - {$IFNDEF NEXTGEN} - Len := {$IFDEF DELPHI_18}{$IFNDEF NEXTGEN}System.AnsiStrings.{$ENDIF}{$ENDIF}StrLen(P); - {$ELSE} - Len := Length(MarshaledAString(P)); - {$ENDIF} - Result := 0; - case FConnect.NativeByteaFormat of - nbfEscape: - begin - I := 0; - while i <= Len - 1 do - begin - if P[i] = '\' then - begin - inc(i); - if P[i] = '\' then - inc(i) - else - inc(i,3); - end - else - inc(i); - inc(Result); - end; - end; - nbfHex: //for >9.0 - begin - Result := (Len - 2) div 2; // '\x' preceding bytea value + 2 hexadecimal digits per byte - end; - end; - end; - -var - Buff : Pointer; - -begin - AField := Fields[FieldNo]; - CheckParam(AField.FieldType <> fldBLOB,DBIERR_NOTABLOB); - AField.Buffer := PRecord; - if not AField.FieldNULL then - begin - Buff := AField.FieldValue; - if PAnsiDACChar(Buff)^ = #1 then - begin - Inc(PAnsiDACChar(Buff)); - iSize := TBlobItem(Buff^).Blob.Size; - end - else - if (AField.NativeBLOBType = nbtOID) or (AField.NativeType = FIELD_TYPE_TEXT) then - iSize := BlobSize(FieldNo, AField.FieldValue) - else - iSize := ByteaSize(FieldNo); - end //not FieldNULL - else - iSize := 0 -end; - -procedure TNativeDataSet.GetBlob(PRecord : Pointer; FieldNo : Word; iOffSet : Longint; iLen : Longint; pDest : Pointer; var iRead : integer); -var - AField : TPSQLField; - - function CachedBlobGet(Offset, Length: longint; buff, Dest: pointer): longint; - begin - if PAnsiDACChar(buff)^ = #1 then - begin - Inc(PAnsiDACChar(buff)); - with TBlobItem(buff^) do - begin - Blob.Seek(Offset, 0); - Result := Blob.Read(Dest^, Length) - end; - end - else - Result := 0; - end; - - function BlobGet(columnNumber: integer; Offset, ALength: LongInt; - Buff, Dest: Pointer): LongInt; - var - L, N: integer; - Len: LongInt; - S: string; - begin - Result := CachedBlobGet(Offset, ALength, Buff, Dest); - if Result = 0 then - if AField.FieldSubType = fldstMemo then - begin - if FConnect.IsUnicodeUsed then - begin - S := FConnect.RawToString(FieldBuffer(columnNumber - 1)); - Len := Length(S) * SizeOf(Char); - {$IFDEF DELPHI_12} - Move((PByte(S) + Offset)^, Dest^, ALength); - {$ELSE} - Move(Pointer(Integer(PByte(S)) + Offset)^, Dest^, ALength); - {$ENDIF} - end - else - begin - {$IFDEF DELPHI_12} - Move((PByte(FieldBuffer(columnNumber - 1)) + Offset)^, Dest^, ALength); - {$ELSE} - Move(Pointer(Integer(PByte(FieldBuffer(columnNumber - 1))) + Offset)^, Dest^, ALength); - {$ENDIF} - Len := FieldSize(columnNumber - 1); - end; - if (Offset + ALength >= Len) then - Result := Len - Offset - else - Result := ALength; - end - else - begin - if FBlobOpen then - begin - lo_lseek(FConnect.Handle, FlocalBHandle, Offset, PG_SEEK_SET); - L := 0; - Len := ALength; - if ALength > MAX_BLOB_SIZE then - begin - repeat - if Len > MAX_BLOB_SIZE then - N := lo_read(FConnect.Handle, FlocalBHandle, - PAnsiDACChar(Dest) + L, MAX_BLOB_SIZE) - else - N := lo_read(FConnect.Handle, FlocalBHandle, - PAnsiDACChar(Dest) + L, Len); - Dec(Len, MAX_BLOB_SIZE); - Inc(L, N); - until N < MAX_BLOB_SIZE; - Result := L; - end - else - Result := lo_read(FConnect.Handle, FlocalBHandle, - PAnsiDACChar(Dest), ALength); - end; - end; - end; - - function ByteaBlobGet(ColumnNumber: Integer; Offset, Length : LongInt; buff, Dest :Pointer) : LongInt; - var P: PAnsiDACChar; - Len: integer; - begin - Result := CachedBlobGet(Offset, Length, Buff, Dest); - if (Result = 0) and - Assigned(PAnsiDACChar(FieldBuffer(columnNumber - 1) + Offset)) then - begin - P := PQUnescapeBytea(FieldBuffer(columnNumber - 1), Len); - try - Move((P + Offset)^, Dest^, Length); - Result := Length; - finally - PQFreeMem(P); - end; - end; - end; - -begin - iRead := 0; - if Assigned(pDest) and (iLen > 0) then - begin - AField := Fields[FieldNo]; - CheckParam(AField.FieldType <> fldBLOB, DBIERR_NOTABLOB); - AField.Buffer := PRecord; - if not AField.FieldNull then - if (AField.NativeBLOBType = nbtOID) or (AField.NativeType = FIELD_TYPE_TEXT) then - iRead := BlobGet(FieldNo, iOffset, iLen, PAnsiDACChar(AField.Data) + AField.FieldNumber - 1 , pDest) - else - iRead := ByteaBLOBGet(FieldNo, iOffset, iLen, PAnsiDACChar(AField.Data) + AField.FieldNumber - 1 ,pDest) - end; -end; - -procedure TNativeDataSet.PutBlob(PRecord: Pointer; FieldNo: Word; iOffSet: Longint; iLen: Longint; pSrc : Pointer); -var - AField : TPSQLField; - - procedure BlobPut(ColumnNumber: Integer; Offset, Length : LongInt; pSrc, buff :Pointer); - begin - if PAnsiDACChar(buff)^ = #0 then - begin - PAnsiDACChar(buff)^ := #1; - Inc(PAnsiDACChar(buff)); - TBlobItem(buff^).Blob := TMemoryStream.Create; - end - else - Inc(PAnsiDACChar(buff)); - with TBlobItem(buff^) do - begin - Blob.Seek(Offset, 0); - if Length > 0 then - Blob.Write(pSrc^, Length) - else - if Offset = 0 then Blob.Clear; - end; - end; - -begin - AField := Fields[FieldNo]; - CheckParam(AField.FieldType <> fldBLOB,DBIERR_NOTABLOB); - AField.Buffer := PRecord; - BlobPut(FieldNo, iOffset, iLen, pSrc, PAnsiDACChar(AField.Data) + AField.FieldNumber-1); - AField.FieldChanged := True; - AField.FieldNull := (iOffset + iLen = 0); -end; - -procedure TNativeDataSet.TruncateBlob(PRecord : Pointer; FieldNo : Word; iLen : Longint); -begin - PutBlob(PRecord, FieldNo, 0, iLen, nil); -end; - -procedure TNativeDataSet.QuerySetParams(Params : TParams; SQLText : String); -var - Token, Temp, Value: string; - Param: TParam; - i: integer; - byName: boolean; - MS: {$IFDEF DELPHI_17}TBytesStream{$ELSE}TMemoryStream{$ENDIF}; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} - - function GetDateTime: string; - var ts: string; - begin - case Param.DataType of - ftDate: ts := 'mm-dd-yyyy'; - ftDateTime: ts := 'mm-dd-yyyy hh:nn:ss.zzz'; - ftTime: ts := 'hh:nn:ss'; - end; - if VarType(Param.Value) = VarDate then - Result := AnsiQuotedStr(FormatDateTime(ts, Param.Value, PSQL_FS),'''') - else - Result := AnsiQuotedStr(VarAsType(Param.Value, varString),''''); - end; - -begin - if Params.Count = 0 then Exit; - Temp := ''; - i := 0; - while SQLText <> '' do - begin - if (Temp <> '') and - {$IFNDEF NEXTGEN} - CharInSet(SQLText[1], [' ',#9]) then Temp := Temp + ' '; - {$ELSE} - SQLText[START_STR_INDEX].IsInArray([' ',#9]) then Temp := Temp + ' '; - {$ENDIF} - GetToken(SQLText, Token); - //Added: handle of ? params - if (Token = ':') or (Token = '?') then - begin - ByName := False; - if Token = ':' then - begin - GetToken(SQLText, Token); - if (length(Token) = 1) and - {$IFNDEF NEXTGEN} - CharInSet(Token[1], [':','=']) then //handling of double colon & assignment - {$ELSE} - Token[START_STR_INDEX].IsInArray([':','=']) then //handling of double colon & assignment - {$ENDIF} - begin - Temp := Temp + Token; - Continue; - end; - ByName := True; - end; - if (Token <> '') and (Token[START_STR_INDEX] = '[') then - begin - if Token[Length(Token)] = ']' then - Token := Copy(Token, 2, Length(Token)-2) - else - Token := Copy(Token, 2, Length(Token)-1); - end else - if (Token <> '') and - {$IFNDEF NEXTGEN} - CharInSet(Token[1], ['"','''']) then - {$ELSE} - Token[START_STR_INDEX].IsInArray(['"','''']) then - {$ENDIF} - begin - if Token[START_STR_INDEX] = Token[Length(Token)] then - Token := Copy(Token, 2, Length(Token)-2) - else - Token := Copy(Token, 2, Length(Token)-1); - end; - // if Params is set with ":" then select param by name - Param := nil; - if ByName then - Param := Params.FindParam(Token) - else - begin - if i < Params.Count then Param := Params[i]; - Inc(i); - end; - if not Assigned(Param) or (VarType(Param.Value) = varEmpty) or (VarType(Param.Value) = varNull) then - Value := 'NULL' - else - case Param.DataType of - ftADT: Value := 'DEFAULT'; - ftBLOB: begin - MS := {$IFDEF DELPHI_17}TBytesStream{$ELSE}TMemoryStream{$ENDIF}.Create; - try - MS.SetSize(Longint(Param.GetDataSize)); - if MS.Size > 0 then - Param.GetData(MS.{$IFDEF DELPHI_17}Bytes{$ELSE}Memory{$ENDIF}); - Value := BlobValue(MS, TPSQLParam(Param).DataTypeOID <> FIELD_TYPE_OID, True); - finally - MS.Free; - end; - end; - ftDate, ftTime, ftDateTime: Value := GetDateTime; - {$IFDEF DELPHI_12} - ftFMTBcd: if VarIsFMTBcd(Param.Value) then - Value := BcdToStr(VarToBcd(Param.Value), PSQL_FS) - else - Value := AnsiQuotedStr(VarAsType(Param.Value, varString),''''); - {$ENDIF} - else - case VarType(Param.Value) of - {$IFNDEF DELPHI_5} - varInt64, - {$ENDIF} - varSmallint, - varInteger, - varByte : Value := IntToStr(Param.Value); - varSingle, - varDouble, - varCurrency : Value := SQLFloatToStr(VarAsType(Param.Value, varDouble)); - varBoolean : if Param.Value then Value := 'TRUE' else Value := 'FALSE'; - else - {$IFDEF DELPHI_12} - if FConnect.IsUnicodeUsed then - Value := StrValue(PWideChar(Param.AsString)) - else - {$ENDIF} - {$IFNDEF NEXTGEN} - Value := StrValue(PAnsiDACChar(AnsiString(Param.AsString))); - {$ELSE} - Value := StrValue(M.AsAnsi(Param.AsString).ToPointer); - {$ENDIF} - end; - end; - Temp := Temp + Value; - end else - Temp := Temp + Token; - end; - SQLQuery := Trim(Temp); -end; - -procedure TNativeDataSet.RelRecordLock(bAll: Boolean); -begin - FIsLocked := FALSE; -end; - -procedure TNativeDataSet.ExtractKey(PRecord: Pointer;pKeyBuf: Pointer); -var - i : Word; - MKey : PAnsiDACChar; - AField : TPSQLField; - bBlank : Boolean; - Buffer : array [0..MAX_CHAR_LEN] of Char; - iFields : Word; -begin - if not Assigned(PRecord) then PRecord := CurrentBuffer; - {$IFNDEF NEXTGEN} - ZeroMemory(pKeyBuf, FKeyDesc.iKeyLen); - {$ELSE} - FillChar(pKeyBuf^, FKeyDesc.iKeyLen, 0); - {$ENDIF} - MKey := pKeyBuf; - iFields := FKeyDesc.iFldsinKey; - for i := 0 to iFields-1 do - begin - AField := Fields[FKeyDesc.aiKeyFld[i]]; - NativeToDelphi(AField, PRecord, @Buffer, bBlank); - if not bBlank then - AdjustDelphiField(AField, @Buffer, MKey); - if bBlank then - {$IFNDEF NEXTGEN} - ZeroMemory(MKey, AField.NativeSize); - {$ELSE} - FillChar(MKey^, AField.NativeSize, 0); - {$ENDIF} - Inc(MKey, AField.NativeSize + 1); - end; -end; - - -procedure TNativeDataSet.GetIndexDesc(iIndexSeqNo: Word; var idxDesc: IDXDesc); -begin - CheckParam(not(IndexCount > 0) ,DBIERR_NOASSOCINDEX); - Finalize(idxDesc); - {$IFNDEF NEXTGEN} - ZeroMemory(@idxDesc, Sizeof(idxDesc)); - {$ELSE} - FillChar(idxDesc, Sizeof(idxDesc), 0); - {$ENDIF} - if (iIndexSeqNo = 0) and not FGetKeyDesc then - if KeyNumber <> 0 then iIndexSeqNo := KeyNumber; - if iIndexSeqNo = 0 then iIndexSeqNo := 1; - CheckParam(FIndexDescs.mIndex[iIndexSeqNo] = nil,DBIERR_NOSUCHINDEX); - idxDesc := FIndexDescs.mIndex[iIndexSeqNo].Description; -end; - -procedure TNativeDataSet.GetIndexDescs(Descs: TIDXDescList); -var - Props : CURProps; - i : Word; -begin - GetCursorProps(Props); - if Props.iIndexes > 0 then - begin - FGetKeyDesc := TRUE; - try - for i := 1 to Props.iIndexes do - GetIndexDesc(i, Descs[i-1]); - finally - FGetKeyDesc := FALSE; - end; - end; -end; - -procedure TNativeDataSet.SwitchToIndex( pszIndexName, pszTagName : string; iIndexId : Word; bCurrRec : Boolean); - -procedure ParseIndexName(pszIndexName: string; Var iIndexId : Word; pszTrueName : string); -var - //S : ShortString; - Found : Boolean; - Desc : IDXDesc; - s: String; -begin - Found := False; - FGetKeyDesc := TRUE; - try - iIndexId := 1; - Repeat - GetIndexDesc ( iIndexId, Desc ); - s:= Desc.szName; - if Desc.szName = pszIndexName then - begin - Found := TRUE; - break; - end; - Inc(iIndexId); - Until Found; - if Found and ( iIndexId > 0 ) and ( pszTrueName <> '' ) then - pszTrueName := Desc.szName; - finally - FGetKeyDesc := False; - end; -end; - -begin - FIsLocked := FALSE; - //CheckParam(pszIndexName = '', DBIERR_INVALIDPARAM); - if FFieldDescs.Count = 0 then InitFieldDescs; - if Length(pszIndexName) > 0 then - ParseIndexName(pszIndexName, iIndexId, '') - else - if FPrimaryKeyNumber >= 1 then iIndexId := FPrimaryKeyNumber; - try - if Ranges then ResetRange; - KeyNumber := iIndexId; - finally - AutoReExec := True; - end; - GetIndexDesc(iIndexId, FKeyDesc); -end; - -procedure TNativeDataSet.ResetRange; -begin - RangeClause.Clear; - if Ranges then ReOpenTable; - Ranges := False; -end; - -procedure TNativeDataSet.SetRange(bKeyItself: Boolean; - iFields1: Word;iLen1: Word;pKey1: Pointer;bKey1Incl: Boolean; - iFields2: Word;iLen2: Word;pKey2: Pointer;bKey2Incl: Boolean); - - procedure CreateRangeClause(First : Boolean; bKeyItself: Boolean;iFields: Word;iLen: Word; pKey: Pointer; bKeyIncl: Boolean); - var - i : integer; - AField : TPSQLField; - WHERE : String; - FldVal : String; - bBlank : Boolean; - Buff : array[0..MAX_CHAR_LEN] of AnsiDACByteChar; - CurBuffer : PAnsiDACChar; - TimeStamp: TTimeStamp; - begin - For i := 0 to iFields-1 do - if Fields[FKeyDesc.aiKeyFld[i]].FieldNull then - begin - RangeClause.Text := 'WHERE 1=0'; - Exit; //null values have no details by standard - end; - - WHERE := ''; - CurBuffer := PAnsiDACChar(pKey); - for i := 0 to iFields-1 do - begin - AField := Fields[FKeyDesc.aiKeyFld[i]]; - if bKeyItself then - AdjustNativeField(AField, CurBuffer, @Buff, bBlank) - else - NativeToDelphi(AField, CurBuffer, @Buff, bBlank); - Inc(CurBuffer, AField.NativeSize + 1); - if bBlank then Continue; //19.05.2008 - if RangeClause.Count > 0 then WHERE := 'and ' else WHERE := 'where '; - WHERE := WHERE + AnsiQuotedStr(AField.FieldName,'"'); - if First then WHERE := WHERE + '>' else WHERE := WHERE + '<'; - if bKeyIncl then WHERE := WHERE + '='; - case AField.Fieldtype of - fldINT16: FldVal := IntToStr(PSmallInt(@Buff)^); - fldINT32: FldVal := IntToStr(PLongInt(@Buff)^); - fldFLOAT: FldVal := SQLFloatToStr(PDouble(@Buff)^); - fldBOOL: if PBoolean(@Buff)^ {$IFDEF FPC} <> 0{$ENDIF} then FldVal := 'True' else FldVal := 'False'; - fldZSTRING: FldVal := StrValue(@Buff); - fldUUID: FldVal := UuidValue(@Buff); - fldINT64: FldVal := IntToStr(PInt64(@Buff)^); - fldDate: - begin - TimeStamp.Date := PLongInt(@Buff)^; - TimeStamp.Time := 0; - FldVal := '''' + DateTimeToSqlDate(TimeStampToDateTime(TimeStamp),1) + ''''; - end; - fldTime: begin - TimeStamp.Date := DateDelta; - TimeStamp.Time := PLongInt(@Buff)^; - FldVal := '''' + DateTimeToSqlDate(TimeStampToDateTime(TimeStamp),2) + ''''; - end; - fldTIMESTAMP: FldVal := '''' + DateTimeToSqlDate(TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF}(PDouble(@Buff)^))),0) + ''''; - end; - WHERE := WHERE + Trim(FldVal); - RangeClause.Add(WHERE); - end; - end; - -begin - try - RangeClause.Clear; - Ranges := True; - CreateRangeClause(True,bKeyItself, iFields1, iLen1, pKey1, bKey1Incl); - CreateRangeClause(False,bKeyItself, iFields2, iLen2, pKey2, bKey2Incl); - ReOpenTable; - except - ResetRange; - end; -end; - -procedure TNativeDataSet.SetKeyNumber( newValue : SmallInt ); -var - x,y : Integer; - Ind : TPSQLIndex; - -function GetOrderByStr(Idx : TPSQLIndex; index : integer) : String; -var - B : Boolean; -begin - result := ''; - B := idx.Descending; - Result := AnsiQuotedStr(string(FieldInfo[idx.FDesc.aiKeyFld[index]-1].FieldName), '"'); - if B then - Result := Result +' DESC'; -end; - - -begin - if newValue <> FKeyNumber then - begin - OrderClause.Clear; - if newValue <= IndexCount then - begin - OrderClause.Add('ORDER BY '); - Ind := FIndexDescs.mIndex[newValue]; - y := ind.FDesc.iFldsInKey-1; - for x := 0 to y-1 do - OrderClause.Add(GetOrderByStr(Ind, x) + ','); - OrderClause.Add(GetOrderByStr(Ind, y)); - end; - FKeyNumber := newValue; - ReOpenTable; - end; -end; - -procedure TNativeDataSet.ReOpenTable; -var OldRowsAffected: integer; -begin - OldRowsAffected := FAffectedRows; - OpenTable; - FAffectedRows := OldRowsAffected; -end; - -procedure TNativeDataSet.ClearIndexInfo; -begin - if FIndexDescs.Count > 0 then - begin - FIndexDescs.Clear; - FIndexDescs.Updated := False; - end; - FKeyNumber := 0; - FPrimaryKeyNumber := 0; -end; - -procedure TNativeDataSet.SettoSeqNo(iSeqNo: Longint); -begin - if iSeqNo - 1 < 0 then - RecNo := -1 - else - if iSeqNo - 1 >= RecordCount - 1 then - RecNo := RecordCount - 1 - else - RecNo := iSeqNo - 1; - CurrentRecord(RecNo); -end; - - -procedure TNativeDataSet.EmptyTable; -var - S : String; - Result : PPGResult; -begin - S := Format('TRUNCATE TABLE %s',[TableName]); - FAffectedRows := 0; - if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; - Result := _PQexecute(FConnect, S); - if Result <> nil then - begin - FConnect.CheckResult; - PQClear(Result); - end else - FConnect.CheckResult; -end; - -procedure TNativeDataSet.AddIndex(var IdxDesc: IDXDesc; pszKeyviolName : string); -var - Result : PPGResult; - - function CreateSQLForAddIndex: String; - var - Fld : String; - PSQLIdxs : TPSQLIndexes; - begin - Result := ''; - PSQLIdxs := TPSQLIndexes.Create(nil); - TPSQLIndex.CreateIndex(PSQLIdxs,@IdxDesc); - Fld := SQLCreateIdxStr(PSQLIdxs[1],TableName,Fields); - Result := Result+Fld; - PSQLIdxs.Free; - end; - -begin - if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; - Result := _PQexecute(FConnect, CreateSQLForAddIndex); - if Result <> nil then - begin - PQclear(Result); - end else - FConnect.CheckResult; -end; - -procedure TNativeDataSet.DeleteIndex(pszIndexName: string; pszIndexTagName: string; iIndexId: Word); -var - Result : PPGResult; -begin - if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; - Result := _PQexecute(FConnect, Format('DROP INDEX %s ON %s',[pszIndexName, TableName])); - if Result <> nil then - begin - PQclear(Result); - end else - FConnect.CheckResult; -end; - -procedure TNativeDataSet.AcqTableLock(eLockType: word; bNoWait: boolean); -const _lockmode: array[TPSQLLockType] of DACAString = ('ACCESS SHARE', 'ROW SHARE', - 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', - 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', - 'ACCESS EXCLUSIVE'); - _nowait: array[boolean] of DACAString = ('', 'NOWAIT'); -var Res: PPGresult; -begin - Res := _PQExecute(FConnect, Format('LOCK TABLE %s IN %s MODE %s', [TableName, _lockmode[TPSQLLockType(eLockType)], _nowait[bNoWait]])); - try - FConnect.CheckResult(Res); - finally - PQclear(RES); -end; -end; - -procedure TNativeDataSet.SetToKey(eSearchCond: DBISearchCond; bDirectKey: Boolean;iFields: Word;iLen: Word;pBuff: Pointer); -var - FldNo : Integer; - AField : TPSQLField; - Item : TPSQLIndex; - R : LongInt; - I : Integer; - Flds : array of integer; - SFlds : array of String; - -begin - Item := FIndexDescs.mIndex[iFields]; - SetLength(Flds,Item.Description.iFldsInKey); - SetLength(SFlds,Item.Description.iFldsInKey); - for I :=0 to Item.Description.iFldsInKey-1 do - begin - FldNo := Item.Description.aiKeyFld[I]; - AField := Fields[FldNo]; - Flds[i] := FldNo-1; - SFlds[I] := FieldVal(AField.FieldNumber,AField.FieldValue); - end; - R := findrows(Flds,SFlds,False,ilen); - if (R <> -1) then - SetToSeqNo(R+1) else - SetToSeqNo(RecordCount); -end; - - -procedure TNativeDataSet.Clone(bReadOnly : Boolean; bUniDirectional : Boolean; var hCurNew : hDBICur); -begin - if FConnect = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); - TNativeConnect(FConnect).OpenTable(TableName, FIndexName, 0, FOMode, dbiOPENSHARED, hCurNew, [], 0, 0); - TNativeDataSet(hCurNew).MasterCursor := Self; -end; - -procedure TNativeDataSet.SetToCursor(hDest : hDBICur); -var - M : Pointer; -begin - if hDest = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); - M := AllocMem(BookMarkSize); - try - if MasterCursor = nil then - begin - GetBookMark(M); - TNativeDataSet(hDest).SetToBookMark(M); // set main cursor bookmark - end; - finally - FreeMem(M, BookMarkSize); - end; -end; - - - -////////////////////////////////////////////////////////////////////// -// TIndexList Object // -////////////////////////////////////////////////////////////////////// -constructor TIndexList.Create(PSQL: TNativeConnect; D : TIDXDescList; TotalCount : integer ); -begin - inherited Create(PSQL, [], '', '', 0, 0, 0); - Items := TotalCount; - if D <> nil then - begin - SetLength(Descs, Items); - Descs := Copy(D, Low(D), Length(D)); - end; - SetToBegin; -end; - -procedure TIndexList.SetToBegin; -begin - inherited SetToBegin; - Position := 0; -end; - -Destructor TIndexList.Destroy; -begin - Finalize(Descs); - Inherited Destroy; -end; - -procedure TIndexList.GetNextRecord(eLock: DBILockType; PRecord : Pointer;pRecProps : pRECProps); -begin - if Position = Items then - raise EPSQLException.CreateBDE(DBIERR_EOF) - else - GetIdxDesc(PRecord); - Inc(Position); -end; - -procedure TIndexList.GetIdxDesc(Precord: PIdxDesc); -begin - PRecord^ := TIDXDescList(Descs)[Position]; -end; - - -function TIndexList.GetBufferSize : integer; -begin - Result := SizeOf(idxDESC); -end; - -function TIndexList.GetWorkBufferSize : integer; -begin - Result := GetBufferSize; -end; - -procedure TIndexList.SetToBookmark(P : Pointer); -begin - SetToBegin; -end; - -procedure TIndexList.GetRecordCount( Var iRecCount : integer ); -begin - iRecCount := Items; -end; - -constructor TFieldList.Create(PSQL: TNativeConnect; D: TFLDDescList; - TotalCount: integer); -begin - inherited Create(PSQL, [], '', '', 0, 0, 0); - Items := TotalCount; - if D <> nil then - begin - SetLength(Descs, Items); - Descs := Copy(D, Low(D), Length(D)); - end; - SetToBegin; - -end; - -destructor TFieldList.Destroy; -begin - Finalize(Descs); - inherited Destroy; -end; - -function TFieldList.GetBufferSize : integer; -begin - Result := SizeOf(FLDDesc); -end; - -{******************************************************************************} -{ TPSQLEngine } -{******************************************************************************} -constructor TPSQLEngine.Create(P : TObject; Container : TContainer); -begin - Inherited Create(P, Container); - FDatabase := DAChDBIDb(Self); -end; - -Destructor TPSQLEngine.Destroy; -begin - FDatabase := nil; - Inherited Destroy; -end; - -function TPSQLEngine.GetDatabase : DAChDBIDb; -begin - Result := FDatabase; -end; - -procedure TPSQLEngine.SetDatabase( H : DAChDBIDb ); -begin - if H = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); - FDatabase := H; -end; - -function TPSQLEngine.IsSqlBased(hDb : DAChDBIDb) : Boolean; -begin - Result := True; -end; - -function TPSQLEngine.OpenDatabase(Params : TStrings; UseSinleLineConnInfo: boolean; var hDb : DAChDBIDb): DBIResult; -Var - DB : TNativeConnect; -begin - FDatabase := nil; - try - Db := TNativeConnect.Create; - if Db = nil then - raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); - try - if UseSinleLineConnInfo then - begin - DB.ProcessDBParams(Params); - Db.InternalConnect; - end - else - DB.InternalConnect(Params); - except - FreeAndNil(DB); - raise; - end; - hDb := DAChDBIDb(DB); - Database := hDb; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.CloseDatabase(var hDb : DAChDBIDb) : DBIResult; -begin - try - Database := hDb; - {$IFNDEF NEXTGEN} - TNativeConnect(hDb).Free; - {$ELSE} - TNativeConnect(hDb).DisposeOf; - {$ENDIF} - hDb := nil; - FDatabase := nil; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenTable(hDb: DAChDBIDb; pszTableName: string; pszIndexName: string; iIndexId: Word; - eOpenMode: DBIOpenMode;eShareMode: DBIShareMode; var hCursor: hDBICur; - AnOptions: TPSQLDatasetOptions; Limit, Offset : Integer): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).OpenTable(pszTableName, pszIndexName, iIndexId, eOpenMode, eShareMode, hCursor, AnOptions, Limit, Offset); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenStoredProcList(hDb: DAChDBIDb;pszWild: string; List : TStrings): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).StoredProcList(pszWild, List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenTableList(hDb: DAChDBIDb;pszWild: string; SystemTables: Boolean; List : TStrings): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).TableList(pszWild, SystemTables, List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenSchemaList(hDb: DAChDBIDb; pszWild: string; SystemSchemas: Boolean; List : TStrings): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).SchemaList(pszWild, SystemSchemas, List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenUserList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).UserList(pszWild, List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetNextRecord(hCursor: hDBICur;eLock: DBILockType;pRecBuff : Pointer;pRecProps: pRECProps): DBIResult; -begin - try - TNativeDataSet(hCursor).GetNextRecord(eLock, pRecBuff, pRecProps); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetToBookMark(hCur: hDBICur;pBookMark: Pointer) : DBIResult; -begin - try - TNativeDataSet(hCur).SetToBookMark(pBookMark); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.CompareBookMarks(hCur : hDBICur;pBookMark1,pBookMark2 : Pointer;Var CmpBkmkResult : CmpBkmkRslt): DBIResult; -begin - try - TNativeDataSet(hCur).CompareBookMarks(pBookMark1, pBookMark2, CmpBkmkResult); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetRecord (hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; -begin - try - TNativeDataSet(hCursor).GetRecord(eLock,PRecord,pRecProps); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetPriorRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; -begin - try - TNativeDataSet(hCursor).GetPriorRecord(eLock, PRecord, pRecProps); - Result := DBIERR_NONE; - except - Result := CheckError; - if Result = DBIERR_EOF then Result := DBIERR_BOF; - end; -end; - -function TPSQLEngine.GetBookMark(hCur: hDBICur;pBookMark : Pointer) : DBIResult; -begin - try - TNativeDataSet(hCur).GetBookMark(pBookMark); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ReadBlock(hCursor : hDBICur; var iRecords : integer; pBuf : Pointer): DBIResult; -begin - try - TNativeDataset(hCursor).ReadBlock(iRecords, pBuf); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetRecordCount(hCursor : hDBICur;Var iRecCount : integer) : DBIResult; -begin - try - TNativeDataSet(hCursor).GetRecordCount(iRecCount); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ForceReread(hCursor: hDBICur): DBIResult; -begin - try - TNativeDataSet(hCursor).ForceReread; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pDest: Pointer;var bBlank: Boolean): DBIResult; -begin - try - TNativeDataSet(hCursor).GetField(FieldNo, PRecord, PDest, bBlank); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.CloseCursor(hCursor : hDBICur) : DBIResult; -begin - try - TNativeDataSet(hCursor).CloseTable; - {$IFDEF NEXTGEN} - TNativeDataSet(hCursor).DisposeOf; - {$ELSE} - TNativeDataSet(hCursor).Free; - {$ENDIF} - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.PutField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pSrc: Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).PutField(FieldNo,PRecord,PSrc); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode): DBIResult; -begin - try - TNativeDataSet(hCursor).OpenBlob(PRecord, FieldNo, eOpenMode); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetBlobSize(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;var iSize: integer): DBIResult; -begin - try - TNativeDataSet(hCursor).GetBlobSize(PRecord, FieldNo, iSize); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint;pDest: Pointer;var iRead: integer): DBIResult; -begin - try - TNativeDataSet(hCursor).GetBlob(PRecord, FieldNo, iOffset, iLen, pDest, iRead); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.Ping(Params: TStrings; - var PingResult: TPingStatus): DBIResult; -begin - try - PingResult := TNativeConnect.Ping(Params); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.PutBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint;pSrc: Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).PutBlob(PRecord, FieldNo, iOffset, iLen, pSrc); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.TruncateBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iLen: Longint): DBIResult; -begin - try - TNativeDataSet(hCursor).TruncateBlob( PRecord, FieldNo, iLen ); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.FreeBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word): DBIResult; -begin - try - TNativeDataSet(hCursor).FreeBlob(PRecord, FieldNo); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.CloseBlob(hCursor: hDBICur; FieldNo: Word): DBIResult; -begin - try - TNativeDataSet(hCursor).CloseBlob(FieldNo); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.BeginTran(hDb: DAChDBIDb; eXIL: eXILType; var hXact: hDBIXact): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).BeginTran(eXIL, hXact); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.EndTran(hDb: DAChDBIDb;hXact : hDBIXact; eEnd : eXEnd): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).EndTran(hXact,eEnd); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetTranInfo(hDb : DAChDBIDb; hXact : hDBIXact; pxInfo : pXInfo): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).GetTranInfo(hXact,pxInfo); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - - -function TPSQLEngine.GetTranStatus(hDb: DAChDBIDb; var TranStatus: TTransactionStatusType): DBIResult; -begin - try - Database := hDb; - TranStatus := TNativeConnect(hDb).GetTransactionStatus; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Pointer;iMaxLen: integer;var iLen: integer): DBIResult; -begin - iLen := 0; - if Assigned(hObj) then - begin - TNativeDataSet(hObj).GetProp( iProp, PropValue, iMaxLen, iLen ); - Result := DBIERR_NONE; - end else - Result := DBIERR_INVALIDPARAM; -end; - -function TPSQLEngine.SetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Longint): DBIResult; -begin - try - if Assigned(hObj) then - begin - TNativeDataSet(hObj).SetProp(iProp, PropValue); - Result := DBIERR_NONE; - end else - Result := DBIERR_INVALIDPARAM; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetVchkDesc(hCursor: hDBICur;iValSeqNo: Word; var pvalDesc: VCHKDesc): DBIResult; -begin - try - TNativeDataSet(hCursor).GetVchkDesc(iValSeqNo, pvalDesc); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetCursorProps(hCursor: hDBICur;var curProps: CURProps): DBIResult; -begin - try - TNativeDataSet(hCursor).GetCursorProps(curProps); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetFieldDescs(hCursor: hDBICur; var pfldDesc : TFLDDescList): DBIResult; -begin - try - TNativeDataSet(hCursor).GetFieldDescs(pFldDesc); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetToBegin(hCursor : hDBICur) : DBIResult; -begin - TNativeDataSet(hCursor).SetToBegin; - Result := DBIERR_NONE; -end; - -function TPSQLEngine.SetToEnd(hCursor : hDBICur) : DBIResult; -begin - TNativeDataSet(hCursor).SetToEnd; - Result := DBIERR_NONE; -end; - -function TPSQLEngine.RelRecordLock(hCursor: hDBICur;bAll: Boolean): DBIResult; -begin - try - TNativeDataSet(hCursor).RelRecordLock(bAll); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.InitRecord(hCursor: hDBICur;PRecord: Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).InitRecord(PRecord); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.InsertRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).InsertRecord(eLock, PRecord); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.AppendRecord(hCursor : hDBICur;PRecord : Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).AppendRecord(PRecord); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ModifyRecord(hCursor: hDBICur;OldRecord,PRecord: Pointer;bFreeLock : Boolean; ARecNo : LongInt): DBIResult; -begin - try - TNativeDataSet(hCursor).ModifyRecord(OldRecord,PRecord, bFreeLock,ARecNo); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.DeleteRecord(hCursor: hDBICur;PRecord: Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).DeleteRecord(PRecord); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetToSeqNo(hCursor: hDBICur;iSeqNo: Longint): DBIResult; -begin - try - TNativeDataSet(hCursor).SettoSeqNo(iSeqNo); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.AddFilter(hCursor: hDBICur;iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr; - pfFilter: pfGENFilter;var hFilter: hDBIFilter): DBIResult; -begin - try - TNativeDataSet(hCursor).AddFilter(iClientData,iPriority, bCanAbort,pcanExpr, pfFilter, hFilter ); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.DropFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; -begin - try - TNativeDataSet(hCursor).DropFilter(hFilter); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ActivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; -begin - try - TNativeDataSet(hCursor).ActivateFilter(hFilter); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.DeactivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; -begin - try - TNativeDataSet(hCursor).DeactivateFilter(hFilter); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetErrorString(rslt: DBIResult; var ErrorMsg: String): DBIResult; -begin - ErrorMsg := MessageStatus; - Result := rslt; -end; - -function TPSQLEngine.QExecDirect(hDb : DAChDBIDb; pszQuery: String;phCur : phDBICur; var AffectedRows : integer): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).QExecDirect(pszQuery,phCur, AffectedRows); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.QAlloc(hDb: DAChDBIDb; var hStmt: hDBIStmt): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).QueryAlloc(hStmt); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.QPrepare(hStmt: hDBIStmt;pszQuery: String): DBIResult; -begin - try - TNativeConnect(Database).QueryPrepare(hStmt,pszQuery); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.QExec(hStmt: hDBIStmt; phCur : phDBICur; var AffectedRows: integer): DBIResult; -begin - try - if phCur = nil then - begin - try - TNativeDataSet(hStmt).Execute; - AffectedRows := TNativeDataSet(hStmt).FAffectedRows; - Result := DBIERR_NONE; - except - Result := CheckError; - end - end - else - begin - TNativeDataSet(hStmt).OpenTable; - if TNativeDataSet(hStmt).FStatement <> nil then - phCur^ := hDBICur(hStmt) - else - phCur^ := nil; - Result := DBIERR_NONE; - end; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.QFree(var hStmt : hDBIStmt): DBIResult; -begin - Result := CloseCursor(hDBICur(hStmt)); - hStmt := nil; -end; - -function TPSQLEngine.QuerySetParams(hStmt: hDBIStmt;Params : TParams; SQLText : String): DBIResult; -begin - try - TNativeDataSet(hStmt).QuerySetParams(Params,SQLText); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -{$IFDEF DELPHI_5} -type - PRaiseFrame = ^TRaiseFrame; - TRaiseFrame = packed record - NextRaise: PRaiseFrame; - ExceptAddr: Pointer; - ExceptObject: TObject; - ExceptionRecord: PExceptionRecord; - end; - -function AcquireExceptionObject: Pointer; -begin - if RaiseList <> nil then - begin - Result := PRaiseFrame(RaiseList)^.ExceptObject; - PRaiseFrame(RaiseList)^.ExceptObject := nil; - end - else - Result := nil; -end; -{$ENDIF} - -function TPSQLEngine.CheckError : DBIResult; -var - ExceptionPtr : Pointer; -begin - Result := DBIERR_NONE; - - ExceptionPtr := AcquireExceptionObject; // ExceptObject; - - if not Assigned(ExceptionPtr) then Exit; - - if TObject(ExceptionPtr) is EPSQLException then - begin - if EPSQLException(ExceptionPtr).BDEErrors then - Result := EPSQLException(ExceptionPtr).BDEErrorCode - else - begin - FNativeStatus := EPSQLException(ExceptionPtr).PSQLErrorCode; - Result := 1001; - end; - FNativeMsg := EPSQLException(ExceptionPtr).PSQLErrorMsg; - TObject(ExceptionPtr).Free; - {$IFDEF DELPHI_7} - ReleaseExceptionObject; - {$ENDIF} - end - else - raise TObject(ExceptionPtr); -end; - -function TPSQLEngine.GetDatabases(hDb: DAChDBIDb; pszWild: string; List : TStrings):DBIResult; -begin - try - Database := hDb; - TNativeConnect(hdb).DatabaseList(pszWild,List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetCharacterSet(hDb : DAChDBIDb; var CharSet : string):DBIResult; -begin - try - Database := hDb; - CharSet := TNativeConnect(hDb).GetCharSet; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -/////////////////////////////////////////////////////////////////////////////// -// Reserver for TPSQLTable // -/////////////////////////////////////////////////////////////////////////////// -function TPSQLEngine.OpenFieldList(hDb: DAChDBIDb; pszTableName: string; pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).OpenFieldList(pszTableName, pszDriverType, bPhyTypes, hCur ); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.OpenIndexList(hDb: DAChDBIDb;pszTableName: string; pszDriverType: string; var hCur: hDBICur): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).OpenIndexList(pszTableName, pszDriverType, hCur); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.EmptyTable(hDb: DAChDBIDb; hCursor : hDBICur; pszTableName : string; pszDriverType : string): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).EmptyTable(hCursor, pszTableName); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetRange(hCursor: hDBICur;bKeyItself: Boolean;iFields1: Word;iLen1: Word;pKey1: Pointer;bKey1Incl: Boolean; - iFields2: Word;iLen2: Word;pKey2: Pointer;bKey2Incl: Boolean): DBIResult; -begin - try - TNativeDataSet(hCursor).SetRange(bKeyItself, iFields1, iLen1, pKey1, bKey1Incl,iFields2, iLen2, pKey2, bKey2Incl); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ResetRange(hCursor: hDBICur): DBIResult; -begin - try - TNativeDataSet(hCursor).ResetRange; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SwitchToIndex(hCursor: hDBICur; pszIndexName, pszTagName: string; iIndexId: Word; bCurrRec: Boolean): DBIResult; -begin - try - TNativeDataSet(hCursor).SwitchToIndex(pszIndexName, pszTagName, iIndexId, bCurrRec); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ExtractKey(hCursor: hDBICur;PRecord: Pointer;pKeyBuf: Pointer): DBIResult; -begin - try - TNativeDataSet(hCursor).ExtractKey(PRecord, pKeyBuf); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetRecordForKey(hCursor: hDBICur; bDirectKey: Boolean; iFields: integer; iLen: integer; pKey: Pointer; pRecBuff: Pointer; AStrictConformity: boolean = False): DBIResult; -begin - try - TNativeDataSet(hCursor).GetRecordForKey(bDirectKey,iFields,iLen, pKey, pRecBuff, AStrictConformity); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.AddIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;var IdxDesc: IDXDesc;pszKeyviolName : string): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDB).AddIndex(hCursor, pszTableName, pszDriverType, idxDesc, pszKeyViolName); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.DeleteIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;pszIndexName: string;pszIndexTagName: string;iIndexId: Word): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDB).DeleteIndex(hCursor, pszTableName, pszDriverType, pszIndexName, pszIndexTagName, iIndexId); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetIndexDesc(hCursor: hDBICur;iIndexSeqNo: Word;var idxDesc: IDXDesc): DBIResult; -begin - try - Result := DBIERR_NONE; - if TNativeDataSet(hCursor).isQuery then - Result := DBIERR_NOASSOCINDEX - else - TNativeDataSet(hCursor).GetIndexDesc(iIndexSeqNo,idxDesc); - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetIndexDescs(hCursor: hDBICur; idxDescs: TIDXDescList): DBIResult; -begin - try - TNativeDataSet(hCursor).GetIndexDescs(idxDescs); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.TranslateRecordStructure(pszSrcDriverType : PChar;iFlds: Word;pfldsSrc: pFLDDesc;pszDstDriverType: PChar; pszLangDriver: PChar;pfldsDst: pFLDDesc; bCreatable: Boolean): DBIResult; -var - M : pFldDesc; - I : Integer; -begin - try - M := pfldsDst; - For i := 1 to iFlds do - begin - Move(pfldsSrc^, M^, SizeOf(FldDesc)); - Inc(M); - Inc(pfldsSrc); - end; - Result :=DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.AcqTableLock(hCursor: hDBICur; eLockType: word; bNoWait: boolean): DBIResult; -begin - try - TNativeDataset(hCursor).AcqTableLock(eLockType, bNoWait); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetToKey(hCursor: hDBICur; - eSearchCond: DBISearchCond; - bDirectKey: Boolean; - iFields: integer; - iLen: integer; - pBuff: Pointer): DBIResult; -begin - try - TNativeDataset(hCursor).SetToKey(eSearchCond, bDirectKey, iFields, iLen, pBuff); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TNativeDataSet.SetRowPosition(iFields: integer; LID: int64; pRecBuffer: Pointer): Boolean; -var - FldNo: integer; - AField: TPSQLField; - Item: TPSQLIndex; - R: LongInt; - i: integer; - Flds: array of integer; - SFlds: array of String; - K: integer; - -var - SS: String; - -begin - if isQuery and (FOMode = dbiREADONLY) then - iFields := -1; // multitable or non-Select SQL query - - if iFields = -1 then - begin - K := 1; - for i := 0 to Fields.Count - 1 do - begin - AField := Fields[i + 1]; - AField.Buffer := pRecBuffer; - if (AField.FieldType = fldBLOB) or (AField.Description.bCalcField) or - (AField.FieldNull and (AField.FieldSubType <> fldstAUTOINC)) or (AField.NativeType = FIELD_TYPE_TIMESTAMP) then - Continue; - SetLength(Flds, K); - SetLength(SFlds, K); - Flds[K - 1] := i; - if (AField.FieldSubType = fldstAUTOINC) and (LID > 0) then - SFlds[K - 1] := inttostr(LID) - else - SFlds[K - 1] := FieldVal(i + 1, AField.FieldValue); - INC(K); - end; - end - else - begin - Item := FIndexDescs.mIndex[iFields]; - SetLength(Flds, Item.Description.iFldsInKey); - SetLength(SFlds, Item.Description.iFldsInKey); - for i := 0 to Item.Description.iFldsInKey - 1 do - begin - FldNo := Item.Description.aiKeyFld[i]; - AField := Fields[FldNo]; - Flds[i] := FldNo - 1; - AField.Buffer := pRecBuffer; - SS := FieldVal(FldNo, AField.FieldValue); - if SS = '' then - begin - if (AField.FieldSubType = fldstAUTOINC) and (LID > 0) then - SS := inttostr(LID); - end; - SFlds[i] := SS; - end; - end; - R := findrows(Flds, SFlds, False, 0); - Result := R <> -1; - if Result then - SettoSeqNo(R + 1); -end; - - -function TPSQLEngine.CloneCursor(hCurSrc: hDBICur;bReadOnly: Boolean;bUniDirectional: Boolean;var hCurNew: hDBICur): DBIResult; -begin - try - TNativeDataset(hCurSrc).Clone(bReadonly, bUniDirectional, hCurNew); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetToCursor(hDest, hSrc : hDBICur) : DBIResult; -begin - try - TNativeDataset(hSrc).SetToCursor(hDest); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -{PSQLNotify} -function TPSQLEngine.OpenPGNotify(hDb: DAChDBIDb; var hNotify: hDBIObj): DBIResult; -//Var -// ANotify : TNativePGNotify; -begin - try - hNotify := nil; - Database := hDB; - {ANotify} hNotify := hDBIObj(TNativePGNotify.Create(TNativeConnect(Database))); - if hNotify = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); -// hNotify := hDBIObj(ANotify); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ClosePGNotify(var hNotify : hDBIObj) : DBIResult; -begin - try - {$IFDEF NEXTGEN} - TNativePGNotify(hNotify).DisposeOf; - {$ELSE} - TNativePGNotify(hNotify).Free; - {$ENDIF} - hNotify := nil; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.ListenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; -begin - try - TNativePGNotify(hNotify).ListenTo(pszEvent); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.UnlistenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; -begin - try - TNativePGNotify(hNotify).UnlistenTo(pszEvent); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.DoNotifyEx(hNotify: hDBIObj; pszChannel: string; pszPayload: string): DBIResult; -begin - try - TNativePGNotify(hNotify).DoNotifyEx(pszChannel, pszPayload); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.DoNotify(hNotify : hDBIObj; pszEvent: string) : DBIResult; -begin - try - TNativePGNotify(hNotify).DoNotify(pszEvent); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.CheckEvents(hNotify : hDBIObj; var Pid : Integer; var pszOutPut, pszPayload : String) : DBIResult; -begin - try - pszOutPut := TNativePGNotify(hNotify).CheckEvents(Pid, pszPayload); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetBackendPID(hDb: DAChDBIDb; var PID: Integer): DBIResult; -begin - try - Database := hDb; - PID := TNativeConnect(hDB).BackendPID; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - - -{****************************************************************************} -{ TNativePGNOTIFY } -{****************************************************************************} -constructor TNativePGNotify.Create(AConnect: TNativeConnect); -begin - FConnect := AConnect; - FHandle:=nil; -end; - -destructor TNativePGNotify.Destroy; -begin - inherited Destroy; -end; - -procedure TNativePGNotify.InternalExecute(Sql: string); -var - locResult : PPGResult; -begin - LocResult := _PQexecute(FConnect, SQL); - if Assigned(LocResult) then - PQclear(LocResult); -end; - -procedure TNativePGNotify.ListenTo(Event: string); -begin - Event := Trim(Event); - if Event <> '' then - InternalExecute('LISTEN ' + Event); -end; - -procedure TNativePGNotify.UnlistenTo(Event: string); -begin - Event := Trim(Event); - if Event <> '' then - InternalExecute('UNLISTEN ' + Event); -end; - -procedure TNativePGNotify.DoNotifyEx(Channel, Payload: string); -begin - Channel := Trim(Channel); - if Channel <> '' then - InternalExecute(Format('NOTIFY %s, %s', [Channel, QuotedStr(Payload)])); -end; - -procedure TNativePGNotify.DoNotify(Event: string); -begin - Event := Trim(Event); - if Event <> '' then - InternalExecute('NOTIFY ' + Event); -end; - -function TNativePGNotify.CheckEvents(var PID : Integer; var Payload: string): string; -begin - Result := ''; - if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; - PQconsumeInput(FConnect.Handle); - FHandle := PQnotifies(FConnect.Handle); - if Assigned(FHandle) then - begin - Result := FConnect.RawToString(FHandle^.relname); - Payload := FConnect.RawToString(FHandle^.extra); - PID := FHandle^.be_pid; - PQfreemem(FHandle); - end; -end; - -function TPSQLEngine.GetServerVersion(hDb: DAChDBIDb; - var ServerVersion: string): DBIResult; -begin - try - Database := hDb; - ServerVersion := TNativeConnect(hDb).GetServerVersion; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetUserProps(hDb: DAChDBIDb; const UserName: string; - var SuperUser, CanCreateDB, - CanUpdateSysCatalogs: boolean; var UserID: integer; - var ValidUntil: string):DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).GetUserProps(UserName, SuperUser, CanCreateDB, - CanUpdateSysCatalogs, UserID, ValidUntil); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetDBProps(hDB: DAChDBIDb; const DB: string; - var Owner, Tablespace: string; - var IsTemplate: boolean; - var DBOid: cardinal; var Comment: string):DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).GetDBProps(DB, Owner, Tablespace, - IsTemplate, DBOid, Comment); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - - -function TNativeConnect.GetServerVersion: string; -begin - if FServerVersion > '' then - begin - Result := FServerVersion; - Exit; - end; - - InternalConnect; - - Result := RawToString(PQparameterStatus(Handle, 'server_version')); - FServerVersion := Result; -end; - -function TNativeDataSet.FieldVal(FieldNo: Integer; FieldPtr : Pointer):String; -var - AField : TPSQLField; - Blank : Boolean; - Buff : array[0..MAX_BLOB_SIZE] of Char; - TimeStamp : TTimeStamp; - DateD : Double; -begin - Result :=''; - AField := Fields[FieldNo]; - AdjustNativeField(AField,FieldPtr,@Buff,Blank); - if not Blank then - case AField.FieldType of - fldINT16: Result := IntToStr(PSmallInt(@Buff)^); - fldUINT16: Result := IntToStr(PWord(@Buff)^); - fldINT32: Result := IntToStr(PLongInt(@Buff)^); - fldINT64: Result := IntToStr(PInt64(@Buff)^); - fldFLOAT: Result := SysUtils.FloatToStr(PDouble(@Buff)^); - //fldBOOL: Result := ifthen( PSmallInt(@Buff)^ > 0, 'T', 'F'); - fldZSTRING: - {$IFDEF DELPHI_12} - if FConnect.IsUnicodeUsed then - Result := PWideChar(@Buff) - else - {$ENDIF} - Result := String(PAnsiDACChar(@Buff)); - fldDATE : begin - TimeStamp.Date := PLongWord(@Buff)^; - TimeStamp.Time := 0; - Result := FormatDateTime('mm-dd-yyyy',SysUtils.Time+Trunc(TimeStampToDateTime(TimeStamp) + 1E-11), PSQL_FS); - end; - fldTIME : begin - TimeStamp.Time := PLongWord(@Buff)^; - TimeStamp.Date := DateDelta; - Result := FormatDateTime('hh:nn:ss',SysUtils.Date+TimeOf(TimeStampToDateTime(TimeStamp)), PSQL_FS); - end; - fldTIMESTAMP : - begin - DateD := PDouble(@Buff)^; - Result := FormatDateTime('mm-dd-yyyy hh:nn:ss',TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF}(DateD))), PSQL_FS); - end; - else - Result := string(DACAString(PAnsiDACChar(@Buff))); - end; -end; - - -procedure TNativeDataSet.GetRecordForKey(bDirectKey: Boolean; - iFields, iLen: integer; - pKey, pRecBuff: Pointer; - AStrictConformity: boolean = False); - - - procedure SetToLookupKey; - var - FieldPtr : Pointer; - FldNo : Integer; - Len : Integer; - R : Longint; - I : Integer; - AField : TPSQLField; - S : String; - Flds : array of Integer; - SFlds : array of String; - begin - S := ''; - Len := 0; - if bDirectKey then - begin - SetLength(Flds,FKeyDesc.iFldsinKey); - SetLength(SFlds,FKeyDesc.iFldsinKey); - for I := 0 to FKeyDesc.iFldsinKey-1 do - begin - FldNo := FKeyDesc.aiKeyFld[i]; - AField := Fields[FKeyDesc.aiKeyFld[i]]; - Flds[i] := FldNo - 1; - FieldPtr := pKey; - Inc(PAnsiDACChar(FieldPtr), Len); - SFlds[i] := FieldVal(FldNo, FieldPtr); - Inc(Len, AField.NativeSize + 1); // AField length in bytes + one byte null indicator - end; - end else - begin - SetLength(Flds,iFields); - SetLength(SFlds,iFields); - for I := 0 to iFields-1 do - begin - FldNo := FKeyDesc.aiKeyFld[I]; - AField := Fields[FKeyDesc.aiKeyFld[I]]; - Flds[I] := FldNo-1; - AField.Buffer := pKey; - SFlds[I] := FieldVal(FldNo, AField.FieldValue); - end; - end; - R := findrows(Flds,SFlds,False,iLen,AStrictConformity); - CheckParam(R=-1 ,DBIERR_RECNOTFOUND); - SettoSeqNo(R+1); - end; - - procedure SetToMasterKey; - var - FieldPtr : Pointer; - FldNo : Integer; - Len : Integer; - R : Longint; - I : Integer; - AField : TPSQLField; - S : String; - Flds : array of Integer; - SFlds : array of String; - begin - S := ''; - Len := 0; - SetLength(Flds,TNativeDataSet(MasterCursor).FKeyDesc.iFldsInKey); - SetLength(SFlds,TNativeDataSet(MasterCursor).FKeyDesc.iFldsInKey); - for I := 0 to TNativeDataSet(MasterCursor).FKeyDesc.iFldsInKey-1 do - begin - FldNo := TNativeDataSet(MasterCursor).FKeyDesc.aiKeyFld[I]; - AField := TNativeDataSet(MasterCursor).Fields[FldNo]; - Flds[I] := FldNo-1; - if bDirectKey then - begin - FieldPtr := pKey; - Inc(PAnsiDACChar(FieldPtr),Len + i); - SFlds[I] := S+FieldVal(FldNo, FieldPtr); - Inc(Len, AField.FieldLength); - end else - begin - AField.Buffer := pKey; - SFlds[i] := FieldVal(FldNo, AField.FieldValue); - end; - end; - R := TNativeDataSet(MasterCursor).findrows(Flds,SFlds,False,iLen,AStrictConformity); - CheckParam(R=-1 ,DBIERR_RECNOTFOUND); - TNativeDataSet(MasterCursor).SettoSeqNo(R+1); - end; - -begin - SetToLookupKey; - if MasterCursor <> nil then - SetToMasterKey; -end; - - -function TNativeDataSet.findrows(const Fields: array of Integer; - const SearchFields: array of String; ACaseSen: Boolean; - APartLen: Integer; AStrictConformity: boolean = False): int64; -var - I, K : Integer; - Cmp : Integer; - IsSorted: boolean; //05.05.2008 - - function Compare1(const S1: String; const S2 : String; FldType : integer):Integer; - - function CompWithLen(const P1, P2 : string): Integer; - begin - Result := Length(P1) - Length(P2); - if Result = 0 then - Result := CompareStr(P1, P2); - end; - - function CompWithoutLen(const P1, P2 : string): Integer; - begin - if AnsiSameStr(P1, Copy(P2, 1, Length(P1))) then - Result := 0 - else - Result := -1;//CompareStr(P1, P2); - end; - - function SqlDateToBDEDateTime(const Value: string): string; - var - Year, Month, Day: String; - Temp: string; - begin - Temp := Value; - Result := ''; - if Length(Temp) >= 10 then - begin - Year := Copy(Temp,1,4); - Month := Copy(Temp,6,2); - Day := Copy(Temp,9,2); - Result := Format('%s-%s-%s',[Month,Day,Year]); - Temp := Copy(Temp,12,8); - end; - if Length(Temp) >= 8 then - Result := Result + ' ' + Temp; - end; - - var BoolChar: string; - - begin - case FldType of - FIELD_TYPE_INT2, - FIELD_TYPE_INT4, - FIELD_TYPE_INT8, - FIELD_TYPE_OIDVECTOR, - FIELD_TYPE_OID: Result := CompWithLen(S1, S2); - - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8, - FIELD_TYPE_NUMERIC: if AStrictConformity then - Result := CompWithLen(StringReplace(S1, PSQL_FS.DecimalSeparator, - '.', [rfReplaceAll]), - S2) - else - Result := CompWithoutLen( - StringReplace(S1, PSQL_FS.DecimalSeparator, - '.', [rfReplaceAll]), - S2); - - FIELD_TYPE_DATE: - if AStrictConformity then - Result := CompWithLen(S1, DateTimeToStr(SqlDateToDateTime(S2, False){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})) - else - Result := CompWithoutLen(S1, DateTimeToStr(SqlDateToDateTime(S2, False){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})); - FIELD_TYPE_TIMESTAMP, - FIELD_TYPE_TIMESTAMPTZ: - if AStrictConformity then - Result := CompWithLen(S1, DateTimeToStr(SQLTimestampToDateTime(S2){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})) - else - Result := CompWithoutLen(S1, DateTimeToStr(SQLTimestampToDateTime(S2){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})); - - FIELD_TYPE_BOOL: begin - BoolChar := IfThen(S1 = '', 'F', 'T'); - Result := Ord(boolchar[1]) - Ord(UpCase(S2[START_STR_INDEX])); - end - - else - if AStrictConformity then - Result := CompWithLen(S1, S2) - else - Result := CompWithoutLen(S1, S2) - end - end; - - function FldVal(CurRow: integer; aIndex: Integer): String; - begin - if IsSorted then CurRow := FSortingIndex[CurRow]; //05.05.2008 - if (aIndex > -1) and (aIndex <= FieldCount-1) then //are we in range? - Result := FConnect.RawToString(PQGetValue(FStatement, CurRow, aIndex)) - else - Result := ''; - end; - -Var - P1,P2 : String; - -Begin - try - Cmp := -1; - IsSorted := IsSortedLocally; - for I := 0 to GetRecCount - 1 do - begin - Cmp := 0; - for K := 0 to High(Fields) do - begin - P1 := SearchFields[K]; - P2 := FldVal(I, Fields[K]); - if not ACaseSen then - begin - P1 := AnsiUpperCase(P1); - P2 := AnsiUpperCase(P2); - end; - Cmp := Cmp + Compare1(P1, P2, FieldType(Fields[K])); - if Cmp <> 0 then Break; - end; - if Cmp = 0 then Break; - end; - if Cmp = 0 then - Result := I - else - Result := -1; - except - Result := -1; - end; -end; - -function TNativeConnect.IsSSLUsed: boolean; -var P: pointer; -begin - Result := False; - if not FLoggin then Exit; - P := PQgetssl(FHandle); - Result := Assigned(P); -end; - -function TNativeConnect.IsTransactionActive: boolean; -begin - Result := FTransState = xsActive; -end; - - -procedure TNativeConnect.BeginBLOBTran; -var - Result: PPGresult; - TransParam: DACAString; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if (FTransState <> xsActive) - AND (GetTransactionStatus = trstIDLE) - then - begin - FBlobTransactionInProgress := True; - Result := PQexec(Handle, 'BEGIN /*BLOB handling*/'); - PQclear(Result); - TransParam := 'SET TRANSACTION ISOLATION LEVEL '; - case FTransLevel of - xilDIRTYREAD, - xilREADCOMMITTED : TransParam := TransParam + 'READ COMMITTED'; - xilREPEATABLEREAD: TransParam := TransParam + 'SERIALIZABLE'; - end; - Result := PQexec(Handle, {$IFNDEF NEXTGEN}PAnsiChar(TransParam){$ELSE}M.AsAnsi(TransParam).ToPointer{$ENDIF}); - PQclear(Result); - end -end; - -procedure TNativeConnect.RollbackBLOBTran; -var - Result: PPGresult; -begin - if FBlobTransactionInProgress AND - (GetTransactionStatus <> trstIDLE) then - begin - FBlobTransactionInProgress := False; - Result := PQexec(Handle, 'ROLLBACK /*BLOB handling*/'); - PQclear(Result); - end -end; - -procedure TNativeConnect.CommitBLOBTran; -var - Result: PPGresult; -begin - if FBlobTransactionInProgress AND - (GetTransactionStatus <> trstIDLE) then - begin - FBlobTransactionInProgress := False; - Result := PQexec(Handle, 'COMMIT /*BLOB handling*/'); - PQclear(Result); - end -end; - -procedure TNativeConnect.StoredProcList(pszWild: string; List: TStrings); -var - CRec : string; - I : LongInt; - sql : String; - RES : PPGresult; -begin - InternalConnect; - List.Clear; - Sql := 'SELECT p.oid, p.oid::regproc' + - ' FROM pg_proc p'; - if pszWild <> '' then - Sql := Sql + ' WHERE p.proname LIKE ' + QuotedStr(pszWild); - Sql := Sql + ' ORDER BY 2'; - RES := _PQexecute(Self, Sql); - if Assigned(RES) then - try - begin - for I := 0 to PQntuples(RES)-1 do - begin - CREC := RawToString(PQgetvalue(RES,I,1)); - RawToString(PQGetValue(Res,I,0)); - {$IFNDEF NEXTGEN} - List.AddObject(CREC,TOBject(strtoint(RawToString(PQGetValue(Res,I,0))))); - {$ELSE} - //this needs for eliminating "segmentation fault" during cast Integer to TObject - // (we increase ref for Integer - this is incorrect with ARC!) - List.AddObject(CREC, TObject( - TPListObject.Create(strtoint(RawToString(PQGetValue(Res,I,0)))) - )); - {$ENDIF} - end; - end; - finally - PQclear(RES); - end; -end; - -function TPSQLEngine.OpenStoredProcParams(hDb: DAChDBIDb; pszPName: string; - ProcOID: cardinal; List: TList{$IFDEF NEXTGEN}{$ENDIF}): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).StoredProcParams(pszPName, ProcOID, List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -procedure TNativeConnect.StoredProcParams(pszPName: string; ProcOID: cardinal; - List: TList{$IFDEF NEXTGEN}{$ENDIF}); -var - PDesc: ^SPParamDesc; - N: string; - I : LongInt; - ProcSchema, ProcName: string; - ArgNum: cardinal; - Sql : String; - MinOIDSel: string; - RES : PPGresult; - BdeType, BdeSubType: integer; - LogSize: integer; - LocArray: boolean; - Status: ExecStatusType; -const - sqlShowParameters = 'SELECT NULLIF(proargnames[g.s],''''), '+ //proargnames[g.s] > 8.0.0 - ' proargtypes[g.s], '+ - ' ''i''::varchar '+ - ' FROM '+ - ' pg_proc p, '+ - ' pg_type t JOIN pg_namespace nsp ON t.typnamespace = nsp.oid , '+ - ' generate_series(1,%d) as g(s) '+ //generate_series(...) > 8.0.0 - ' WHERE '+ - ' p.proargtypes[g.s] = t.oid AND '+ - ' p.oid = %d'; - - sqlShowParameters810 = 'SELECT NULLIF(proargnames[g.s],''''),'+ - ' CASE WHEN t.typtype = ''d''::char THEN' + - ' t.typbasetype' + - ' ELSE' + - ' t.oid' + - ' END,'+ - ' COALESCE(proargmodes[g.s], ''i'') '+ - ' FROM'+ - ' pg_proc p,'+ - ' pg_type t,'+ - ' generate_series(1,%d) as g(s)'+ - ' WHERE'+ - ' COALESCE(p.proargtypes[g.s-1],proallargtypes[g.s]) = t.oid AND'+ - ' p.oid = %d'+ - ' ORDER BY g.s '; -begin - InternalConnect; - List.Clear; - ArgNum := 0; - if GetserverVersionAsInt > 080100 then - MinOIDSel := 'SELECT GREATEST(pronargs, array_upper(proallargtypes,1)) ' - else - MinOIDSel := 'SELECT pronargs '; - if ProcOID = 0 then - begin - I := Pos('.',pszPName); - if I > 0 then - begin - ProcSchema := Copy(pszPName,1,I-1); - ProcSchema := StringReplace(ProcSchema, '"', '', [rfReplaceAll]); - ProcName := Copy(pszPName,I+1,MaxInt); - end - else - begin - ProcName := pszPName; - ProcSchema := '%'; - end; - ProcName := StringReplace(ProcName, '"', '', [rfReplaceAll]); - - MinOIDSel := MinOIDSel + ', pg_proc.oid '+ - 'FROM pg_catalog.pg_proc, pg_catalog.pg_namespace '+ - Format(' WHERE proname = ''%s'''+ - ' AND nspname LIKE ''%s''', - [ProcName,ProcSchema]) + - ' ORDER BY 2 '+ - ' LIMIT 1'; - end - else - MinOIDSel := MinOIDSel + - ' FROM pg_catalog.pg_proc '+ - Format(' WHERE oid = %d', [ProcOID]); - - - - RES := _PQexecute(Self, MinOIDSel); - Status := PQresultStatus(RES); - if (Status = PGRES_TUPLES_OK) and (PQntuples(RES) > 0) then - begin - ArgNum := StrToInt(RawToString(PQgetvalue(RES,0,0))); - if ProcOID = 0 then - ProcOID := StrToIntDef(RawToString(PQgetvalue(RES,0,1)), InvalidOID); - end - else - CheckResult(); - PQclear(Res); - - if ProcOID * ArgNum = 0 then Exit; - - - if GetserverVersionAsInt >= 080100 then - Sql := Format(sqlShowParameters810,[ArgNum,ProcOID]) - else - Sql := Format(sqlShowParameters,[ArgNum,ProcOID]); - - RES := _PQExecute(Self, Sql); - if PQresultStatus(RES) = PGRES_TUPLES_OK then - begin - for I := 0 to PQntuples(RES)-1 do - begin - New(PDesc); - {$IFNDEF NEXTGEN} - ZeroMemory(PDesc,SizeOf(PDesc^)); - {$ELSE} - FillChar(PDesc^,SizeOf(PDesc^), 0); - {$ENDIF} - - if (PQgetisnull(RES,I,0) = 1) then - N := 'arg' + IntToStr(I) - else - N := RawToString(PQgetvalue(RES,I,0)); - PDesc^.szName := N; - PDesc^.uParamNum := I; - FieldMapping(StrToIntDef(RawToString(PQgetvalue(RES,I,1)), InvalidOID), 0, BdeType, BdeSubType, LogSize, LocArray); - PDesc^.uFldType := BdeType; - PDesc^.uSubType := BdeSubType; - N := RawToString(PQgetvalue(RES,I,2)); - case N[START_STR_INDEX] of - 'o': PDesc^.eParamType := paramOUT; - 'b': PDesc^.eParamType := paramINOUT; - else - PDesc^.eParamType := paramIN; - end; - List.Add(PDesc) - end; - end; - PQclear(RES); -end; - -function TNativeConnect.StringToRaw(S: string): PAnsiDACChar; -var - _S: DACAString; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - _S := StringToRawS(S); - DACAllocStr(Result, Length(S)); - DACStrCopy(Result, {$IFNDEF NEXTGEN} - PAnsiDACChar(_S) - {$ELSE} - M.AsAnsi(_S).ToPointer - {$ENDIF} - ); -end; - -function TNativeConnect.StringToRawS(S: string): DACAString; -begin - if IsUnicodeUsed then - Result := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(S)) - else - Result := DACAString(S); -end; - -function TPSQLEngine.QPrepareProc(hDb: DAChDBIDb; pszProc: PChar; - hParams: pointer; var hStmt: hDBIStmt): DBIResult; -var SQLText,ParStr: string; - i: integer; - aParams: TPSQLParams; -begin - try - aParams := TPSQLParams(hParams); - QAlloc(hDb, hStmt); - SQLText := 'SELECT * FROM '+pszProc+'(%s)'; - if (aParams.Count > 0) and (aParams[0].ParamType in [ptInput,ptInputOutput]) then - ParStr := ':' + AnsiQuotedStr(aParams[0].Name, '"') - else - ParStr := ''; - for i := 1 to aParams.Count - 1 do - if aParams[i].ParamType in [ptInput,ptInputOutput] then - ParStr := ParStr + ', :' + AnsiQuotedStr(aParams[i].Name, '"'); - TNativeDataSet(hStmt).SQLQuery := Format(SQLText,[ParStr]); - TNativeDataSet(hStmt).isQuery := True; // PaGo 24.07.2007 - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.QSetProcParams(hStmt: hDBIStmt; Params: TParams): DBIResult; -begin - try - TNativeDataSet(hStmt).StoredProcSetParams(Params); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -procedure TNativeDataSet.StoredProcSetParams(Params: TParams); -begin - QuerySetParams(Params,SQLQuery); -end; - - -procedure TNativeConnect.SetCharSet(var ACharSet: string); -{$IFDEF NEXTGEN} -var - M: TMarshaller; -{$ENDIF} -begin - if (ACharSet = '') or not FLoggin then - begin - ACharSet := GetCharSet(); - Exit; - end; - - PQsetClientEncoding(Handle, {$IFNDEF NEXTGEN} - PAnsiChar(AnsiString(ACharSet)) - {$ELSE} - M.AsAnsi(ACharSet).ToPointer - {$ENDIF} - ); - - ACharSet := GetCharSet(); - - {$IFDEF M_DEBUG} - LogDebugMessage('INFO', 'Encoding changed to ' + ACharset); - {$ENDIF} -end; - -procedure TNativeConnect.GetCharSetList(var List: TStrings); -var - sql : String; - RES : PPGresult; - i: integer; - CREC: string; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - InternalConnect; - List.Clear; - Sql := Format('SELECT pg_encoding_to_char(num.n) FROM generate_series(0,%d) as num(n)', [MAX_ENCODING_ID]); - RES := PQexec(Handle, {$IFNDEF NEXTGEN} - PAnsiChar(AnsiString(Sql)) - {$ELSE} - M.AsAnsi(Sql).ToPointer - {$ENDIF} - ); - if Assigned(RES) then - try - CheckResult; - List.BeginUpdate; - try - for i:=0 to PQntuples(RES)-1 do - begin - CREC := Trim(RawToString(PQgetvalue(RES,i,0))); - if CREC > '' then List.Append(CREC); - end; - finally - List.EndUpdate; - end; - finally - PQclear(RES); - end; -end; - -function TPSQLEngine.SetCharacterSet(hDb: DAChDBIDb; var CharSet: string): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).SetCharSet(CharSet); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetErrorVerbosity(hDb : DAChDBIDb; const ErrorVerbosity: TErrorVerbosity): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).SetErrorVerbosity(ErrorVerbosity); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SetCommandTimeout(hDb : DAChDBIDb; var Timeout : cardinal):DBIResult; -begin - try - Database := hDb; - Timeout := TNativeConnect(hDb).SetTimeout(Timeout); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetCharacterSets(hDb: DAChDBIDb; - List: TStrings): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).GetCharSetList(List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TNativeDataSet.CheckUniqueKey(var KeyNumber : integer): Boolean; -var - I: Integer; - Item : TPSQLIndex; -begin - Result := False; - for I := 1 to FindexDescs.Count do - begin - Item := FIndexDescs.mIndex[I]; - if Item.Primary or Item.Unique then - begin - Result := True; - KeyNumber := I; - Break; - end; - end; -end; - -function TNativeDataSet.GetLastInsertID(const KeyNumber: integer): integer; -var - S: string; - i: integer; - RES: PPGresult; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - Result := -1; - S := FFieldDescs.Field[KeyNumber+1].FieldDefault; - i := Pos('nextval(',lowercase(S)); - if i>0 then - S := StringReplace(S, 'next', 'curr', [rfReplaceAll]) - else - Exit; - S := 'SELECT ' + S; - Res := PQExec(FConnect.Handle, {$IFNDEF NEXTGEN} - PAnsiChar(AnsiString(S)) - {$ELSE} - M.AsAnsi(S).ToPointer - {$ENDIF} - ); - if Assigned(RES) then - try - FConnect.CheckResult; - if PQntuples(RES)>0 then - Result := StrToIntDef(FConnect.RawToString(PQgetvalue(RES,0,0)),-1); - finally - PQclear(RES); - end; -end; - -function TNativeConnect.GetTransactionStatus: TTransactionStatusType; -begin - Result := PQTransactionStatus(FHandle); -end; - -procedure TNativeConnect.GetUserProps(const UserName: string; - var SuperUser, CanCreateDB, - CanUpdateSysCatalogs: boolean; var UserID: integer; - var ValidUntil: string); -var - sql : String; - RES : PPGresult; -begin - SuperUser := False; - CanCreateDB := False; - CanUpdateSysCatalogs := False; - UserID := -1; - ValidUntil := ''; - InternalConnect; - Sql := 'SELECT usesysid, usecreatedb, usesuper, usecatupd, valuntil '+ - ' FROM pg_user WHERE usename = '''+UserName+''''; - - RES := _PQExecute(Self, Sql); - if Assigned(RES) then - try - CheckResult; - if PQntuples(RES) > 0 then - begin - UserID := StrToIntDef(string(PQgetvalue(RES, 0, 0)), InvalidOID); - CanCreateDB := PQgetvalue(RES, 0, 1) = 't'; - SuperUser := PQgetvalue(RES, 0, 2) = 't'; - CanUpdateSysCatalogs := PQgetvalue(RES, 0, 3) = 't'; - ValidUntil := string(PQgetvalue(RES, 0, 4)); - end; - finally - PQclear(RES); - end; -end; - - -procedure TNativeConnect.GUCList(List: TStrings); -var - I: Longint; - Res: PPGresult; -begin - if not FLoggin then Exit; - List.Clear; - Res := PQExec(Handle, 'SHOW ALL'); - try - for I := 0 to PQntuples(Res) - 1 do - List.Values[RawToString(PQgetvalue(Res, I, 0))] := RawToString(PQgetvalue(Res, I, 1)); - finally - PQclear(Res); - end; -end; - -procedure TNativeConnect.GetDBProps(const DB: string; var Owner, - Tablespace: string; var IsTemplate: boolean; var DBOid: cardinal; - var Comment: string); -var - sql : String; - RES : PPGresult; - SV: integer; -begin - DBOid := 0; - IsTemplate := False; - Tablespace := ''; - Owner := ''; - SV := GetServerVersionAsInt; - - if SV >= 080200 then - Sql := Format(' LEFT JOIN %s ON (db.oid = %s.objoid), ', ['pg_shdescription', 'pg_shdescription']) - else - Sql := Format(' LEFT JOIN %s ON (db.oid = %s.objoid), ', ['pg_description', 'pg_description']); - - Sql := 'SELECT db.oid, datistemplate, usename, %s '+ - ' COALESCE(description,'''')::varchar '+ - ' FROM pg_database as db '+ - Sql + - ' %s '+ - ' pg_user as sh '+ - ' WHERE '+ - ' %s '+ - ' sh.usesysid = datdba AND '+ - ' db.datname = '''+DB+''''; - - - - if SV >= 080000 then - Sql := Format(Sql,['spcname,','pg_tablespace as tsp,','tsp.oid = dattablespace AND']) - else - Sql := Format(Sql,['','','']); - - RES := _PQExecute(Self, Sql); - if Assigned(RES) then - try - CheckResult; - if PQntuples(RES) > 0 then - begin - DBOid := StrToInt64(string(PQgetvalue(RES,0,0))); - IsTemplate := PQgetvalue(RES,0,1) = 't'; - Owner := RawToString(PQgetvalue(RES,0,2)); - if SV >= 080000 then - Tablespace := RawToString(PQgetvalue(RES,0,3)); - Comment := RawToString(PQgetvalue(RES,0,4)); - end; - finally - PQclear(RES); - end; -end; - - -procedure TNativeConnect.GetTableProps(const TableName: string; var Owner, - Comment, Tablespace: string; var TableOid: cardinal); -var - sql, Tbl, Schema : String; - I : integer; - RES : PPGresult; -begin - Owner := ''; - Comment := ''; - Tablespace := ''; - TableOid := 0; - - Sql := 'SELECT pg_class.oid, usename, '#13#10 + - ' COALESCE(pg_description.description,''''), COALESCE(pg_tablespace.spcname,'''')'#13#10 + - ' FROM pg_class'#13#10 + - ' INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)'#13#10 + - ' INNER JOIN pg_user ON (pg_class.relowner = pg_user.usesysid)'#13#10 + - ' LEFT JOIN pg_tablespace ON (pg_class.reltablespace = pg_tablespace.oid)'#13#10 + - ' LEFT JOIN pg_description ON (pg_description.objoid = pg_class.oid)'#13#10 + - ' WHERE relkind IN (''r'', ''v'') AND relname = ''%s'' AND nspname LIKE ''%s'''; - - - Tbl := StringReplace(TableName,'"','',[rfReplaceAll]); - I := Pos('.',Tbl); - if I > 0 then - begin - Schema := Copy(Tbl, 1, I-1); - Tbl := Copy(Tbl, I+1, MaxInt); - end else - Schema := '%'; - Sql := Format(Sql, [Tbl, Schema]); - RES := _PQExecute(Self, Sql); - try - if (PQresultStatus(RES) = PGRES_TUPLES_OK) and (PQntuples(RES) > 0) then - begin - TableOid := StrToInt64(RawToString(PQgetvalue(RES, 0, 0))); - Owner := RawToString(PQgetvalue(RES, 0, 1)); - Comment := RawToString(PQgetvalue(RES, 0, 2)); - Tablespace := RawToString(PQgetvalue(RES, 0, 3)); - end; - finally - PQclear(RES); - end; -end; - -function TPSQLEngine.GetTableProps(hDB: DAChDBIDb; const TableName: string; - var Owner, Comment, Tablespace: string; var TableOid: cardinal): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).GetTableProps(TableName, Owner, Comment, - Tablespace, TableOid); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TNativeConnect.GetServerVersionAsInt: integer; -begin - if FIntServerVersion <= 0 then - FIntServerVersion := PQserverVersion(Handle); - Result := FIntServerVersion -end; - - -function TPSQLEngine.GetFieldOldValue(hCursor: hDBICur; AFieldName: string; AParam: TParam): DBIResult; -begin - try - TNativeDataSet(hCursor).FieldOldValue(AFieldName, AParam); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.GetFieldValueFromBuffer(hCursor: hDBICur; - PRecord: Pointer; AFieldName: string; AParam: TParam; const UnchangedAsNull: boolean): DBIResult; -begin - try - TNativeDataSet(hCursor).FieldValueFromBuffer(PRecord, AFieldName, AParam, UnchangedAsNull); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - - -procedure TNativeDataSet.FieldOldValue(AFieldName: string; var AParam: TParam); -var AFNum, Len: integer; - FVal: PAnsiDACChar; -begin - AFNum := FieldIndex(AnsiQuotedStr(AFieldName, '"')); - if AFNum = -1 then Exit; - if FieldIsNull(AFNum) then - begin - AParam.Value := Null; - Exit; - end; - case FieldType(AFNum) of - FIELD_TYPE_BYTEA: - begin - FVal := PQUnescapeBytea(FieldBuffer(AFNum),Len); - try - {$IFDEF DELPHI_17} - AParam.SetBlobData(BytesOf({$IFDEF NEXTGEN}String{$ENDIF}(FVal[0])), Len); - {$ELSE} - AParam.SetBlobData(FVal, Len); - {$ENDIF} - TPSQLParam(AParam).DataTypeOID := FIELD_TYPE_BYTEA; - finally - PQFreeMem(FVal); - end; - end; - FIELD_TYPE_BOOL: AParam.AsBoolean := SameText('t', Field(AFNum)); - else - AParam.Value := Field(AFNum); - end; - if FieldType(AFNum) = FIELD_TYPE_OID then TPSQLParam(AParam).DataTypeOID := FIELD_TYPE_OID; -end; - - -procedure TNativeDataSet.FieldValueFromBuffer(PRecord: Pointer; AFieldName: string; - var AParam: TParam; const UnchangedAsNull: boolean); -var - I : Integer; - Fld : TPSQLField; - Src : Pointer; - -begin - - for I := 1 to FFieldDescs.Count do - begin - Fld := FFieldDescs.Field[I]; - Fld.Buffer:= PRecord; - if CompareText(Fld.FieldName, AFieldName)<>0 then Continue; - Src := Fld.FieldValue; - Inc(PAnsiDACChar(Src)); - if not Fld.FieldChanged and not UnchangedAsNull then //field was not changed, we put there old value - begin - FieldOldValue(AFieldName, AParam); - Exit; - end; - if Fld.FieldNull then - AParam.Value := Null - else - begin - case Fld.FieldType of - fldBOOL: AParam.AsBoolean := Boolean(SmallInt(Src^)); - fldINT16: AParam.AsSmallInt := SmallInt(Src^); - fldINT32: AParam.AsInteger := LongInt(Src^); - fldINT64: begin - {$IFDEF DELPHI_5} - AParam.AsString := IntToStr(Int64(Src^)); - {$ELSE} - AParam.DataType := DataTypeMap[Fld.FieldType]; - AParam.Value := Int64(Src^); - {$ENDIF} - end; - fldFLOAT: AParam.AsFloat := Double(Src^); - fldZSTRING: begin - {$IFDEF DELPHI_12} - if FConnect.IsUnicodeUsed then - AParam.AsString := PWideChar(Src) - else - {$ENDIF} - AParam.AsString := String(PAnsiDACChar(Src)); - if (Fld.NativeType = FIELD_TYPE_BIT) or (Fld.NativeType = FIELD_TYPE_VARBIT) then - AParam.AsString := 'B' + AParam.AsString; - end; - fldUUID: AParam.AsString := String(PAnsiDACChar(Src)); - fldBLOB: if Fld.NativeBLOBType = nbtOID then - begin - AParam.AsInteger := StrToUInt(BlobValue(Src, Fld)); - TPSQLParam(AParam).DataTypeOID := FIELD_TYPE_OID; - end - else - if not Assigned(TBlobItem(Src^).Blob) or (TBlobItem(Src^).Blob.Size = 0) then - AParam.Value := Null - else - if Fld.FieldSubType = fldstMemo then - AParam.AsString := MemoValue(Src, False) - else - AParam.LoadFromStream(TBlobItem(Src^).Blob, ftBlob); - fldDate: AParam.AsDate := TDateTime(Src^); - fldTime: AParam.AsTime := TDateTime(Src^); - fldTIMESTAMP: AParam.AsDateTime := TDateTime(Src^); - {$IFDEF DELPHI_12} - fldFMTBCD: AParam.AsFMTBCD := TBcd(Src^); - {$ENDIF} - end; //case - end; //else - Break; - end; -end; - -function TNativeConnect.GetTimeout: cardinal; -var - RES : PPGresult; -begin - Result := 0; - if GetserverVersionAsInt <= 070302 then - Exit; - InternalConnect; - RES := PQexec(Handle, 'SELECT current_setting(''statement_timeout'')'); - if Assigned(RES) then - try - CheckResult; - if PQntuples(RES) > 0 then - Result := StrToIntDef(RawToString(PQgetvalue(RES,0,0)),0); - finally - PQclear(RES); - end; -end; - -{$HINTS OFF} -procedure TNativeConnect.SetErrorVerbosity(const ErrorVerbosity: TErrorVerbosity); -var OldEV: TErrorVerbosity; -{$IFDEF M_DEBUG} -const EVNames: array[TErrorVerbosity] of DACAString = ('TERSE', 'DEFAULT', 'VERBOSE'); -{$ENDIF} -begin - if FLoggin then - begin - OldEV := PQsetErrorVerbosity(Handle, ErrorVerbosity); - {$IFDEF M_DEBUG} - LogDebugMessage('INFO', Format('Error verbosity changed from %s to %s', [EVNames[OldEV], EVNames[ErrorVerbosity]])); - {$ENDIF} - end; -end; -{$HINTS ON} - -function TNativeConnect.SetTimeout(const Timeout: cardinal): cardinal; -var - S : String; - RES : PPGresult; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - Result := 0; - if GetserverVersionAsInt <= 070302 then - Exit; - InternalConnect; - S := Format('SELECT set_config(''statement_timeout'', ''%d'', false)',[Timeout]); - RES := PQexec(Handle, {$IFNDEF NEXTGEN} - PAnsiChar(AnsiString(S)) - {$ELSE} - M.AsAnsi(S).ToPointer - {$ENDIF} - ); - if Assigned(RES) then - try - CheckResult; - Result := StrToIntDef(string(PQgetvalue(Res, 0, 0)), Timeout); - finally - PQclear(RES); - end; -end; - -function TPSQLEngine.GetCommandTimeout(hDb: DAChDBIDb; - var Timeout: cardinal): DBIResult; -begin - try - Database := hDb; - Timeout := TNativeConnect(hDb).GetTimeout; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - - -function TNativeDataSet.HasFieldTimeZone(const FldNum: integer): boolean; -begin - case FieldType(FldNum-1) of - FIELD_TYPE_TIMETZ, - FIELD_TYPE_TIMESTAMPTZ: Result := True; - else - Result := False; - end; -end; - -function TNativeDataSet.FieldTable(FieldNum: integer): cardinal; -begin - if FStatement <> nil then - Result := PQftable(FStatement,FieldNum) - else - Result := InvalidOid; -end; - -function TNativeDataSet.FieldOrigin(FieldNum: integer): string; -var TabOid: cardinal; - ColNum: integer; - s: string; - IsOK: boolean; -begin - Result := ''; - if FStatement <> nil then - begin - TabOid := FieldTable(FieldNum - 1); //pg_attribute uses 1 as first field index, but low level rotines not - if TabOid <= InvalidOid then Exit; - ColNum := FieldPosInTable(FieldNum - 1); - s := Format('SELECT %u::regclass || ''.'' || quote_ident(attname) '+ - 'FROM pg_attribute WHERE attrelid = %u AND attnum = %d', - [TabOid, TabOid, ColNum]); - Result := FConnect.SelectStringDirect(s, IsOK, 0); - end; -end; - -function TNativeDataSet.FieldPosInTable(FieldNum: integer): Integer; -begin - if FStatement <> nil then - begin - Result := PQftablecol(FStatement,FieldNum); - if Result = 0 then - Result := -1; - end - else - Result := -1; -end; - -function TPSQLEngine.GetLastInsertId(hCursor: hDBICur; - const FieldNum: integer; var ID: integer): DBIResult; -begin - try - ID := TNativeDataset(hCursor).GetLastInsertID(FieldNum); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -procedure TNativeDataSet.SortBy(FieldNames: string; - Compare: TPSQLDatasetSortCompare); -begin - FCustomCompareFunc := Compare; - try - SortBy(FieldNames); - finally - FCustomCompareFunc := nil; - end; -end; - -procedure TNativeDataSet.SortBy(FieldNames: string); -var - a, cnt, i : integer; - str : string; - AFields : array of integer; - IsReverseOrder : array of boolean; -const - sAsc : string = ' ASC'; - sDesc : string = ' DESC'; -begin - FSortingFields := Trim(FieldNames); - - if FSortingFields = '' then - begin - SetLength(FSortingIndex,0); - Exit; - end; - - cnt := 0; - - for i:=1 to Length(FieldNames) do - begin - if FieldNames[i] = ',' then Inc(cnt);//count number of AFields - - if FieldNames[i] = #9 then - FieldNames[i] := ' ';//replace TABs to SPACEs - end; - - SetLength(AFields, cnt + 1); - SetLength(IsReverseOrder, cnt + 1); - - i := 0; - if cnt > 0 then//multi-AFields sorting - while Pos(',', FieldNames) <> 0 do - begin - a := Pos(',', FieldNames); - str := Trim(copy(FieldNames, 1, a - 1)); - Delete(FieldNames, 1, a); - - if AnsiUpperCase(copy(str, Length(str) - Length(sDesc) + 1, Length(sDesc))) = sDesc then - begin - IsReverseOrder[i] := true; - Delete(str, Length(str) - Length(sDesc) + 1, Length(sDesc)); - end - else if AnsiUpperCase(copy(str, Length(str) - Length(sAsc) + 1, Length(sAsc))) = sAsc then - begin - IsReverseOrder[i] := false; - Delete(str, Length(str) - Length(sAsc) + 1, Length(sAsc)); - end - else - begin - IsReverseOrder[i] := false; - end; - - a := FieldIndex(Trim(str));//trying to find dield in AFields definitions - if a = -1 then - begin - DatabaseError(Format(SFieldNotFound, [Str])); - FSortingFields := ''; - SetLength(FSortingIndex,0); - end; - AFields[i] := a; - Inc(i); - end; - - //single field sorting (or last field sorting) - str := Trim(FieldNames); - - if AnsiUpperCase(copy(str, Length(str) - Length(sDesc) + 1, Length(sDesc))) = sDesc then - begin - IsReverseOrder[i] := true; - Delete(str, Length(str) - Length(sDesc) + 1, Length(sDesc)); - end - else if AnsiUpperCase(copy(str, Length(str) - Length(sAsc) + 1, Length(sAsc))) = sAsc then - begin - IsReverseOrder[i] := false; - Delete(str, Length(str) - Length(sAsc) + 1, Length(sAsc)); - end - else - begin - IsReverseOrder[i] := false; - end; - Str := Trim(str); - a := FieldIndex(str);//trying to find field in AFields definitions - if a = -1 then - begin - DatabaseError(Format(SFieldNotFound, [str])); - FSortingFields := ''; - SetLength(FSortingIndex,0); - end; - AFields[i] := a; - - InternalSortBy(AFields, IsReverseOrder); -end; - -procedure TNativeDataSet.InternalSortBy(const Fields: array of Integer; - const IsReverseOrder: array of boolean); - -var aRecNum: integer; - i: integer; - - function StringToVariant(S: string; NativeType: oid): variant; - begin - case NativeType of - FIELD_TYPE_INT2, - FIELD_TYPE_INT4, - FIELD_TYPE_INT8: - {$IFDEF DELPHI_5} - Result := StrToIntDef(S, 0); - {$ELSE} - Result := StrToInt64Def(S, 0); - {$ENDIF} -{$IFDEF UNDER_DELPHI_12} - FIELD_TYPE_NUMERIC, -{$ENDIF} - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: - try - Result := StrToFloat(S, PSQL_FS); - except - //D5 have no StrToFloatDef - on E: EConvertError do - Result := 0.0; - end; -{$IFDEF DELPHI_12} - FIELD_TYPE_NUMERIC: - Result := VarFMTBcdCreate(S, High(Word), High(Word)); -{$ENDIF} - -FIELD_TYPE_BOOL: Result := S[START_STR_INDEX] = 't'; - - FIELD_TYPE_OID: if dsoOIDAsInt in FOptions then - {$IFDEF DELPHI_5} - Result := StrToIntDef(S, InvalidOid) - {$ELSE} - Result := StrToInt64Def(S, InvalidOid) - {$ENDIF} - else - Result := 0; - FIELD_TYPE_TEXT, - FIELD_TYPE_BYTEA: Result := 0; //BLOB's are not comparable - - else - //datetime fields will be compared here also - //cause we have ISO output datestyle: yyyy-mm-dd hh:mm:ss[-tz] - Result := S; - end; - - end; - - function CustomCmpRecords(Index1, Index2: integer): integer; - var - V1, V2: Variant; - Idx1IsNull, Idx2IsNull, i: integer; - begin - Result := 0; - for i:= Low(Fields) to High(Fields) do - begin - Idx1IsNull := PQGetIsNull(FStatement, FSortingIndex[Index1], Fields[I]); - Idx2IsNull := PQGetIsNull(FStatement, FSortingIndex[Index2], Fields[I]); - if Idx1IsNull + Idx2IsNull = 2 then Exit; //no need to compare two NULLs - if Idx1IsNull = 1 then - V1 := Null - else - V1 := StringToVariant(FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index1], Fields[I])), - PQFType(FStatement, Fields[I])); - if Idx2IsNull = 1 then - V2 := Null - else - V2 := StringToVariant(FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index2], Fields[I])), - PQFType(FStatement, Fields[I])); - Result := FCustomCompareFunc(nil, V1, V2, Fields[I]); - if Result <> 0 then Break; - end; - end; - - function DefaultCmpRecords(Index1, Index2: integer): integer; - var i, Idx1IsNull, Idx2IsNull: integer; - FVal1, FVal2: string; - begin - Result := 0; - for i:= Low(Fields) to High(Fields) do - begin - Idx1IsNull := PQGetIsNull(FStatement, FSortingIndex[Index1], Fields[I]); - Idx2IsNull := PQGetIsNull(FStatement, FSortingIndex[Index2], Fields[I]); - case Idx1IsNull + Idx2IsNull of - 2: Result := 0; - 1: Result := Idx1IsNull - Idx2IsNull; - else - begin - FVal1 := FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index1], Fields[I])); - FVal2 := FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index2], Fields[I])); - case PQFType(FStatement, Fields[I]) of - FIELD_TYPE_INT2, - FIELD_TYPE_INT4, - FIELD_TYPE_INT8: Result := StrToInt64Def(FVal1, 0) - - StrToInt64Def(FVal2, 0); -{$IFDEF UNDER_DELPHI_12} - FIELD_TYPE_NUMERIC, -{$ENDIF} - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: - try - Result := Sign(StrToFloat(FVal1, PSQL_FS) - - StrToFloat(FVal2, PSQL_FS)); - except - //D5 have no StrToFloatDef - on E: EConvertError do - Result := 0; - end; -{$IFDEF DELPHI_12} - FIELD_TYPE_NUMERIC: Result := BcdCompare(StrToBcd(FVal1, PSQL_FS), StrToBcd(FVal2, PSQL_FS)); -{$ENDIF} - FIELD_TYPE_BOOL: Result := ord(FVal1[START_STR_INDEX]) - - ord(FVal2[START_STR_INDEX]); - - FIELD_TYPE_OID: if dsoOIDAsInt in FOptions then - Result := StrToIntDef(FVal1, InvalidOid) - - StrToIntDef(FVal2, InvalidOid) - else - Result := 0; - FIELD_TYPE_TEXT, - FIELD_TYPE_BYTEA: Result := 0; //BLOB's are not comparable - - else - //datetime fields will be compared here also - //cause we have ISO output datestyle: yyyy-mm-dd hh:mm:ss[-tz] - Result := AnsiCompareStr(FVal1, FVal2); - end; - end; - end; - if IsReverseOrder[i] then - Result := -Result; - if Result <> 0 then Break; - end; - end; - - procedure SwapIndexes(Index1, Index2: integer); - var T: integer; - begin - T := FSortingIndex[Index1]; - FSortingIndex[Index1] := FSortingIndex[Index2]; - FSortingIndex[Index2] := T; - end; - - function CmpRecords(Index1, Index2: integer): integer; - begin - if not Assigned(FCustomCompareFunc) then - Result := DefaultCmpRecords(Index1, Index2) - else - Result := CustomCmpRecords(Index1, Index2); - end; - - procedure QuickSort(L, R: Integer); - var - I, J, P: Integer; - begin - repeat - I := L; - J := R; - P := (L + R) shr 1; - repeat - while CmpRecords(I, P) < 0 do - Inc(I); - while CmpRecords(J, P) > 0 do - Dec(J); - if I <= J then - begin - SwapIndexes(I, J); - if P = I then - P := J - else if P = J then - P := I; - Inc(I); - Dec(J); - end; - until I > J; - if L < J then QuickSort(L, J); - L := I; - until I >= R; - end; - -begin - aRecNum := GetRecCount; - if (High(Fields) = -1) or (aRecNum < 2) then - Exit; - SetLength(FSortingIndex, aRecNum); - - for i := 0 to aRecNum - 1 do //initialization - FSortingIndex[i] := i; - - QuickSort(Low(FSortingIndex), High(FSortingIndex)); -end; - -function TNativeDataSet.GetRecNo: integer; -var - nGetRecCount: Integer; -begin - Result := RecNo; - if not IsSortedLocally then Exit; - nGetRecCount := GetRecCount(); - if nGetRecCount > 0 then - if (High(FSortingIndex) = nGetRecCount-1) then Result := FSortingIndex[RecNo]; -end; - -function TNativeDataSet.IsSortedLocally: boolean; -begin - Result := (High(FSortingIndex) > -1) and (High(FSortingIndex) = GetRecCount-1); //19.05.2008 -end; - -procedure TPSQLIndexes.SetNeedUpdate(const Value: boolean); -begin - FUpdated := Value; -end; - -function TNativeDataSet.CheckCanLive: boolean; -var i: integer; - TabOID: cardinal; -begin - - Result := False; - - if FConnect = nil then - exit; - - if not IsQuery then //assume tables are editable by default - begin - Result := True; - Exit; - end; - - TabOID := FieldTable(0); - if TabOID = InvalidOid then Exit; - for i:=1 to FieldCount-1 do - if (TabOID = InvalidOid) or (TabOID <> FieldTable(i)) then - Exit - else - TabOID := FieldTable(i); - - Result := True; //all checks passed -end; - -//>> pasha_golub 10.08.06 -function TPSQLEngine.CheckBuffer(hCursor: hDBICur; - PRecord: Pointer): DBIResult; -begin - try - if TNativeDataSet(hCursor).FCurrentBuffer = PRecord then - TNativeDataSet(hCursor).FCurrentBuffer:= nil; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; -//<< pasha_golub 10.08.06 - -function TPSQLEngine.OpenTablespaceList(hDb: DAChDBIDb; pszWild: string; - List: TStrings): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).TablespaceList(pszWild, List); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - - -procedure TNativeConnect.TablespaceList(pszWild: string; List: TStrings); -var - CRec : string; - I : LongInt; - sql : String; - RES : PPGresult; -begin - InternalConnect; - Sql := 'SELECT spcname '+ - ' FROM "pg_tablespace" '; - if pszWild <> '' then - Sql := Sql + ' WHERE spcname LIKE ' + AnsiQuotedStr(pszWild,''''); - Sql := Sql + ' ORDER BY 1'; - RES := _PQExecute(Self, Sql); - try - if Assigned(RES) then - begin - CheckResult; - for I := 0 to PQntuples(RES)-1 do - begin - CRec := RawToString(PQgetvalue(RES,I,0)); - List.Add(CRec); - end; - end; - finally - PQclear(RES); - end; -end; - -function TNativeDataSet.UuidValue(P : Pointer; NeedQuote: boolean = True): string; -var AVal: PAnsiDACChar; -begin - Result := ''; - if not Assigned(P) then Exit; - AVal := PAnsiDACChar(P); - Result := FConnect.RawToString(AVal); - if NeedQuote then Result := '''' + Result + ''''; -end; - -function TNativeDataSet.StrValue(P : Pointer; NeedQuote: boolean = True):String; -var - Buffer, AVal : PAnsiDACChar; - SZ, Err : Integer; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - Result := ''; - if P <> nil then - begin - {$IFDEF DELPHI_12} - if FConnect.IsUnicodeUsed then - {$IFNDEF NEXTGEN} - AVal := PAnsiDACChar(FConnect.StringToRawS(PWideChar(P))) - {$ELSE} - AVal := M.AsAnsi(FConnect.StringToRawS(PWideChar(P))).ToPointer - {$ENDIF} - else - {$ENDIF} - AVal := PAnsiDACChar(P); - - if not NeedQuote then - begin - Result := FConnect.RawToString(AVal); - Exit; - end; - - {$IFNDEF NEXTGEN} - SZ := {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrLen(AVal); - {$ELSE} - SZ := Length(MarshaledAString(@AVal)); //1 byte for #0 character - {$ENDIF} - - GetMem(Buffer, 2*SZ+1); - try - {$IFNDEF NEXTGEN} - ZeroMemory(Buffer, 2*SZ+1); - {$ELSE} - FillChar(Buffer^, 2*SZ+1, 0); - {$ENDIF} - PQEscapeStringConn(FConnect.Handle, Buffer, AVal, SZ, Err); - if Err > 0 then - FConnect.CheckResult; - Result := '''' + FConnect.RawToString(Buffer) + ''''; - finally - FreeMem(Buffer); - end; - end; -end; - -function TNativeDataSet.MemoValue(P : Pointer; NeedQuote: boolean = True):String; -var - Buffer : PAnsiDACChar; - SZ : Integer; -begin - Result := ''; - if TBlobItem(P^).Blob <> nil then - begin - if TBlobItem(P^).Blob.Size = 0 then exit; - SZ := TBlobItem(P^).Blob.Size + SizeOf(Char); //null termination - GetMem(Buffer, SZ); - {$IFNDEF NEXTGEN} - ZeroMemory(Buffer,SZ); - {$ELSE} - FillChar(Buffer^,SZ, 0); - {$ENDIF} - TBlobItem(P^).Blob.Seek(Longint(0), 0); - TBlobItem(P^).Blob.Read(Buffer^, SZ); - Result := StrValue(Buffer, NeedQuote); - FreeMem(Buffer, SZ); - end; -end; - -function TNativeDataSet.BlobValue(P : Pointer; Fld: TPSQLField; NeedEscape: boolean = True): string; -begin - Result := BlobValue(TBlobItem(P^).Blob, Fld.NativeBLOBType = nbtBytea, NeedEscape); -end; - -function TNativeDataset.BlobValue(MS: TStream; isBytea: boolean; NeedEscape: Boolean = True): string; -var - Buffer, PEsc : PAnsiDACChar; - SZ : Integer; - Res : LongInt; - Off, BlSZ: Integer; -begin - Result := '0'; - if not Assigned(MS) then Exit; - - if MS.Size = 0 then - begin - if isBytea then - if NeedEscape then Result := '''''' else Result := '' - else - begin - FConnect.BeginBLOBTran; - try - FBlobHandle := lo_creat(FConnect.Handle, INV_WRITE or INV_READ); - if FBLobHandle = 0 then - raise EPSQLException.CreateMsg(FConnect,'Can''t create BLOB! lo_creat operation failed!'); - Result := UIntToStr(FBlobHandle); - finally - FConnect.CommitBLOBTran; - end; - end; - Exit; - end; - - SZ := MS.Size; - GetMem(Buffer, SZ + 1); - {$IFNDEF NEXTGEN} - ZeroMemory(Buffer, SZ + 1); - {$ELSE} - FillChar(Buffer^, SZ + 1, 0); - {$ENDIF} - MS.Seek(Longint(0), 0); - MS.Read(Buffer^, SZ); - if isBytea then - begin - if NeedEscape then - begin - PEsc := PQEscapeByteaConn(FConnect.Handle, Buffer, SZ, BlSZ); - try - Result := '''' + FConnect.RawToString(PEsc) + ''''; - finally - PQFreeMem(PEsc); - end; - end - else - Result := string(Buffer); - FreeMem(Buffer, SZ+1); - end - else //nbtOID in other case - begin - FConnect.BeginBLOBTran; - FBlobHandle := lo_creat(FConnect.Handle,INV_WRITE or INV_READ); - if FBlobHandle = 0 then - begin - FConnect.RollbackBLOBTran; - raise EPSQLException.CreateMsg(FConnect,'Can''t create BLOB! lo_creat operation failed!') - end; - FLocalBHandle := lo_open(FConnect.Handle, FBlobHandle, INV_WRITE); - try - Off := 0; - repeat - BlSZ := Min(MAX_BLOB_SIZE, SZ - off); - Res := lo_write(FConnect.Handle, FLocalBHandle, Buffer + off, BLSZ); - if Res < 0 then - raise EPSQLException.CreateMsg(FConnect,'BLOB operation failed!') - else - Inc(Off, Res); - until (off >= SZ); - FreeMem(Buffer, SZ+1); - except - lo_close(FConnect.Handle,FlocalBHandle); - FConnect.RollbackBLOBTran; - raise; - end; - lo_close(FConnect.Handle,FlocalBHandle); - FConnect.CommitBLOBTran; - Result := UIntToStr(FBlobHandle); - end; -end; - -function TPSQLEngine.QGetProcParams(hStmt: hDBIStmt; - Params: TParams): DBIResult; -begin - try - TNativeDataSet(hStmt).StoredProcGetParams(Params); - Result := DBIERR_NONE; - except - Result := CheckError; - end; - -end; - -procedure TNativeDataSet.StoredProcGetParams(Params: TParams); -var i,j: integer; -begin - if not Assigned(FStatement) then Exit; - for i:=0 to Params.Count-1 do - if Params[i].ParamType in [ptOutput, ptInputOutput] then - for j := 0 to FieldCount - 1 do - if Params[i].Name = FieldName(j) then - Params[i].AsString := Field(j); -end; - -function TNativeConnect.RawToString(S: PAnsiDACChar): string; -begin - if IsUnicodeUsed then -{$IFDEF DELPHI_12} - Result := UTF8ToUnicodeString(S) -{$ELSE} - Result := UTF8ToString(S) -{$ENDIF} - else - Result := string(S); -end; - -{$IFDEF DELPHI_15} -procedure ReverseBytes(P: Pointer; Count: Integer); -var - P1: PByte; - P2: PByte; - C: Byte; -begin - P1 := PByte(P); - P2 := PByte(P) + Count - 1; - while P1 < P2 do - begin - C := P1^; - P1^ := P2^; - P2^ := C; - System.inc(P1); - System.dec(P2); - end; -end; - -function TNativeConnect.BinaryToString(S: PAnsiDACChar; TypeOID: cardinal): string; -begin - case TypeOID of - FIELD_TYPE_INT4: - begin - ReverseBytes(S, SizeOf(Integer)); - Result := IntToStr(PInteger(S)^); - end; - FIELD_TYPE_INT2: Result := IntToStr(Swap(Smallint(S))); - end; -end; -{$ENDIF} - - -procedure TNativeConnect.ReloadGUC; -begin - GUCList(FGUCList); -end; - -procedure TNativeConnect.Reset; -begin - PQreset(FHandle); -end; - -function TPSQLEngine.Reset(hDb: DAChDBIDb): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDB).Reset; - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.CancelBackend(hDb: DAChDBIDb; PID: Integer): DBIResult; -begin - try - Database := hDb; - TNativeConnect(hDb).CancelBackend(PID); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -procedure TNativeConnect.CancelBackend(PID: Integer); -var - RES: PPGresult; - sql: DACAString; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - InternalConnect; - sql := DACAString(Format('SELECT pg_cancel_backend(%u)',[PID])); - RES := PQexec(Handle, {$IFNDEF NEXTGEN} - PAnsiChar(sql) - {$ELSE} - M.AsAnsi(sql).ToPointer - {$ENDIF} - ); - try - CheckResult; - finally - PQClear(RES); - end; -end; - -function TPSQLEngine.SelectStringDirect(hDb: DAChDBIDb; pszQuery: PChar; - var IsOk: boolean; var aResult: string; - aFieldNumber: integer): DBIResult; -begin - try - aResult := TNativeConnect(hDB).SelectStringDirect(pszQuery, IsOk, aFieldNumber); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SelectStringDirect(hDb: DAChDBIDb; pszQuery: PChar; - var IsOk: boolean; var aResult: string; aFieldName: string): DBIResult; -begin - try - aResult := TNativeConnect(hDB).SelectStringDirect(pszQuery, IsOk, PChar(aFieldName)); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TNativeConnect.SelectStringDirect(pszQuery: string; - var IsOk: boolean; aFieldNumber: integer): string; -var - Stmt : PPGresult; -begin - Result := ''; - InternalConnect; - - Stmt := _PQExecute(Self, pszQuery); - try - IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and - (PQnfields(Stmt) > aFieldNumber) and - (aFieldNumber >= 0) and - (PQntuples(Stmt) > 0); - if IsOK then - Result := RawToString(PQgetvalue(Stmt,0,aFieldNumber)); - {else - CheckResult;} - finally - PQClear(Stmt); - end; -end; - -function TNativeConnect.SelectStringDirect(pszQuery: string; - var IsOk: boolean; pszFieldName : string): string; -var - Stmt : PPGresult; - P: PAnsiDACChar; -begin - Result := ''; - InternalConnect; - - Stmt := _PQExecute(Self, pszQuery); - try - P := StringToRaw(pszFieldName); - IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and - (PQfnumber(Stmt, P) > -1) and - (PQntuples(Stmt) > 0); - if IsOK then - Result := RawToString(PQgetvalue(Stmt,0,PQfnumber(Stmt, P))); - - {$IFNDEF NEXTGEN} - DACAnsiStrDispose(P); - {$ENDIF} - finally - PQClear(Stmt); - end; -end; - -function TPSQLEngine.GetFieldTypeOID(hCursor: hDBICur; const FieldNum: integer): cardinal; -begin - Result := TNativeDataset(hCursor).FieldType(FieldNum); -end; - -procedure TFieldList.GetFLDDesc(PRecord: pFLDDesc); -begin - PRecord^ := TFLDDescList(Descs)[Position]; -end; - -procedure TFieldList.GetNextRecord(eLock: DBILockType; PRecord: Pointer; - pRecProps: pRECProps); -begin - if Position = Items then - raise EPSQLException.CreateBDE(DBIERR_EOF) - else - GetFLDDesc(PRecord); - Inc(Position); -end; - - -procedure TFieldList.GetRecordCount(var iRecCount: Integer); -begin - iRecCount := Items; -end; - -function TFieldList.GetWorkBufferSize: integer; -begin - Result := GetBufferSize; -end; - -procedure TFieldList.SetToBegin; -begin - inherited SetToBegin; - Position := 0; -end; - -procedure TFieldList.SetToBookmark(P: Pointer); -begin - SetToBegin; -end; - -function TPSQLEngine.GetFieldOrigin(hCursor: hDBICur; - const FieldNum: integer): string; -begin - Result := TNativeDataset(hCursor).FieldOrigin(FieldNum); -end; - -function TPSQLEngine.SelectStringsDirect(hDb: DAChDBIDb; pszQuery: PChar; - aList: TStrings; aFieldNumber: integer): DBIResult; -begin - try - TNativeConnect(hDB).SelectStringsDirect(pszQuery, aList, aFieldNumber); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TPSQLEngine.SelectStringsDirect(hDb: DAChDBIDb; pszQuery: PChar; - aList: TStrings; aFieldName: string): DBIResult; -begin - try - TNativeConnect(hDB).SelectStringsDirect(pszQuery, aList, aFieldName); - Result := DBIERR_NONE; - except - Result := CheckError; - end; -end; - -function TNativeConnect.SelectStringsDirect(pszQuery: string; - aList: TStrings; aFieldNumber: integer): string; -var - Stmt : PPGresult; - i: integer; - IsOK: boolean; -begin - Result := ''; - InternalConnect; - - Stmt := _PQExecute(Self, pszQuery); - try - IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and - (PQnfields(Stmt) > aFieldNumber) and - (PQntuples(Stmt) > 0); - if IsOK then - try - aList.BeginUpdate; - for i := 0 to PQntuples(Stmt) - 1 do - aList.Append(RawToString(PQgetvalue(Stmt, i, aFieldNumber))); - finally - aList.EndUpdate; - end - else - CheckResult; - finally - PQClear(Stmt); - end; -end; - - -function TNativeConnect.SelectStringsDirect(pszQuery: string; - aList: TStrings; pszFieldName: string): string; -var - Stmt : PPGresult; - P: PAnsiDACChar; - i, ColNum: integer; - IsOK: boolean; -begin - Result := ''; - InternalConnect; - - Stmt := _PQExecute(Self, pszQuery); - try - P := StringToRaw(pszFieldName); - ColNum := PQfnumber(Stmt, P); - IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and - (ColNum > -1) and - (PQntuples(Stmt) > 0); - if IsOK then - try - aList.BeginUpdate; - for i := 0 to PQntuples(Stmt) - 1 do - aList.Append(RawToString(PQgetvalue(Stmt, i, ColNum))); - finally - aList.EndUpdate; - end - else - CheckResult; - {$IFNDEF NEXTGEN} - DACAnsiStrDispose(P); - {$ENDIF} - finally - PQClear(Stmt); - end; -end; - - -{$IFDEF NEXTGEN} -{ TPListObject } - -constructor TPListObject.Create(Value: Integer); -begin - FValue := Value; -end; - -class operator TPListObject.Implicit(obj: TPListObject): Integer; -begin - Result := obj.FValue; -end; -{$ENDIF} - -destructor TPSQLIndex.Destroy; -begin - Finalize(FDesc); - inherited; -end; - -initialization - - {$IFDEF M_DEBUG} - OpenDebugFile; - {$ENDIF} - - {$IFDEF DELPHI_15} - PSQL_FS := TFormatSettings.Create(); - {$ELSE} - {$IFNDEF FPC} - GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, PSQL_FS); - {$ENDIF} - {$ENDIF} - PSQL_FS.DecimalSeparator := '.'; //for use inside StrToFloat - PSQL_FS.TimeSeparator := ':'; //for use inside FormatDateTime - -finalization - - {$IFDEF M_DEBUG} - CloseDebugFile; - {$ENDIF} - -end. +{$I pSQLDAC.inc} + +unit PSQLAccess; + +{SVN revision: $Id$} + +{$T-} + +interface + +uses Classes, Db, PSQLTypes, Math, + {$IFDEF FPC}FMTBcd, LCLIntf,{$ENDIF} + {$IFDEF DELPHI_12}PSQLGeomTypes, {$ENDIF} + {$IFDEF DELPHI_9}DbCommon,{$ELSE}PSQLCommon,{$ENDIF} + {$IFDEF DELPHI_6}FmtBcd, Variants,{$ENDIF} + {$IFDEF FPC}Variants,{$ENDIF} + {$IFDEF NEXTGEN}Generics.Collections,{$ENDIF} + SysUtils; + +{$IFDEF DELPHI_12} + {$NOINCLUDE PSQLGeomTypes} +{$ENDIF} + +type + {$IFDEF NEXTGEN} + TPListObject = class + private + FValue: Integer; + public + class operator Implicit(obj: TPListObject): Integer; + constructor Create(Value: Integer); overload; + end; + {$ENDIF} + + {Forward declaration} + TNativeConnect = class; + +{****************************************************************************} +{ Error handler } +{****************************************************************************} + EPSQLException = Class(EAbort) + private + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FPSQL : TNativeConnect; + FPSQLErrorCode : Word; + FBDEErrorCode : Word; + FBDE : Boolean; + FPSQLErrorMsg : String; + public + constructor CreateBDE(ECode : Word); + constructor CreateBDEMsg(ECode : Word; Const EMessage : String); + constructor Create(PSQL : TNativeConnect); + constructor CreateMsg(PSQL : TNativeConnect; Const ErrorMsg : String ); + property PSQLErrorCode : word read FPSQLErrorCode; + property PSQLErrorMsg : String read FPSQLErrorMsg; + property BDEErrorCode : Word read FBDEErrorCode; + property BDEErrors : Boolean read FBDE; + end; + +{****************************************************************************} +{ TNativeConnect } +{****************************************************************************} + TNativeConnect = Class(TObject) + private + FHandle : PPGconn; + FSystem : Boolean; + FLastOperationTime : cardinal; + FBlobTransactionInProgress: boolean; + FUnicodeUsed : boolean; + FCharset : string; + FServerVersion : string; + FIntServerVersion : integer; + FConnectString : string; + FDirectConnectString : string; + FTransState : eXState; { Transaction end control xsActive, xsInactive } + FTransLevel : eXILType; { Transaction isolation levels } + FGUCList : TStrings; {GUC parameters as key=value list} + function GetBackendPID : integer; + function IsTransactionActive: boolean; + function GetTransactionStatus: TTransactionStatusType; + function GetNativeByteaFormat: TNativeByteaFormat; + public + FErrorPos : string; + FErrorContext : string; + FErrorSeverity : string; + FErrorSQLState : string; + FErrorDetail : string; + FErrorPrimary : string; + FErrorHint : string; + FErrorInternalPos : string; + FErrorInternalQuery : string; + FErrorSourceFile : string; + FErrorSourceLine : string; + FErrorSourceFunc : string; + FErrorSchemaName : string; + FErrorTableName : string; + FErrorColumnName : string; + FErrorDataTypeName : string; + FErrorConstraintName : string; + + FLoggin : Boolean; {Loggin flag} + DBOptions : TDBOptions; {Connection parameters} + constructor Create; + destructor Destroy; Override; + + class function Ping(Params: TStrings): TPingStatus; + + procedure DirectExecute(SQL: String); + procedure ProcessDBParams(Params : TStrings); + procedure InternalConnect(ConnParams: TStrings = nil); {Login to database} + procedure InternalDisconnect; {Logout from database} + procedure ReloadGUC; + procedure Reset; {reset connection to server} + function Rollback: boolean; {Rollback transaction} + function Commit: boolean; {Commit transaction} + procedure CancelBackend(PID: Integer); + procedure CheckResult; overload;{Check result last operation} + procedure CheckResult(FStatement: PPGresult); overload; + function GetErrorText: String; {Get Error text} + function Success: Boolean; + procedure StoredProcParams(pszPName: string; ProcOID: cardinal; + List: TList{$IFDEF NEXTGEN}{$ENDIF}); + procedure GUCList(List : TStrings); + procedure StoredProcList(pszWild : string; List : TStrings); + procedure TableList(pszWild : string; SystemTables: Boolean; List : TStrings); + procedure UserList(pszWild : string; List : TStrings); + procedure SchemaList(pszWild : string; SystemSchemas: Boolean; List : TStrings); + procedure TablespaceList(pszWild : string; List : TStrings); + procedure DatabaseList(pszWild : string; List : TStrings); + procedure OpenTable(pszTableName: string; pszIndexName: string; iIndexId: Word; + eOpenMode: DBIOpenMode;eShareMode: DBIShareMode;var hCursor: hDBICur; + AnOptions: TPSQLDatasetOptions; + Limit, Offset : Integer); + procedure QueryAlloc(var hStmt: hDBIStmt); + procedure QueryPrepare(var hStmt: hDBIStmt;Query : String); + procedure BeginTran(eXIL: eXILType; var hXact: hDBIXact); + procedure BeginBLOBTran; + procedure RollbackBLOBTran; + procedure CommitBLOBTran; + procedure EndTran(hXact : hDBIXact; eEnd : eXEnd); + procedure GetTranInfo(hXact : hDBIXact; pxInfo : pXInfo); + procedure QExecDirect(pszQuery : String; phCur: phDBICur; var AffectedRows : integer); + procedure OpenFieldList(pszTableName: string; pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur); + procedure OpenIndexList(pszTableName: string; pszDriverType: string; var hCur: hDBICur); + function GetCharSet: string; + procedure GetCharSetList(var List: TStrings); + procedure SetCharSet(var ACharSet: string); + function GetTimeout: cardinal; + function SetTimeout(const Timeout: cardinal): cardinal; + procedure SetErrorVerbosity(const ErrorVerbosity: TErrorVerbosity); + function GetServerVersion: string; + function GetserverVersionAsInt: integer; + procedure GetUserProps(const UserName: string; var SuperUser, CanCreateDB, + CanUpdateSysCatalogs: boolean; var UserID: integer; + var ValidUntil: string); + procedure GetDBProps(const DB: string; + var Owner, Tablespace: string; + var IsTemplate: boolean; + var DBOid: cardinal; var Comment: string); + procedure GetTableProps(const TableName: string; + var Owner, Comment, Tablespace: string; + var TableOid: cardinal); + procedure EmptyTable(hCursor : hDBICur; pszTableName : string); + procedure AddIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; var IdxDesc: IDXDesc; pszKeyviolName: string); + procedure DeleteIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; pszIndexName: string; pszIndexTagName: string; iIndexId: Word); + + property IsolationLevel : eXILType Read FTransLevel; + property Handle : PPGconn read FHandle write FHandle; + property BackendPID : Integer read GetBackendPID; + property LastOperationTime: cardinal read FLastOperationTime; + property InTransaction: boolean read IsTransactionActive; + property TransactionStatus: TTransactionStatusType read GetTransactionStatus; + property BlobTransactionInProgress: boolean read FBlobTransactionInProgress; + property NativeByteaFormat: TNativeByteaFormat read GetNativeByteaFormat; + property IsUnicodeUsed: boolean read FUnicodeUsed; + property GUC: TStrings read FGUCList; + + function SelectStringDirect(pszQuery : string; var IsOk : boolean; aFieldNumber : integer):string; overload; + function SelectStringDirect(pszQuery : string; var IsOk : boolean; pszFieldName : string):string; overload; + function SelectStringsDirect(pszQuery : string; aList : TStrings; aFieldNumber : integer):string; overload; + function SelectStringsDirect(pszQuery : string; aList : TStrings; pszFieldName : string):string; overload; + + + function IsSSLUsed: boolean; + + function RawToString(S: PAnsiDACChar): string; + function StringToRaw(S: string): PAnsiDACChar; //need to be free by StrDispose + function StringToRawS(S: string): DACAString; +{$IFDEF DELPHI_15} + function BinaryToString(S: PAnsiDACChar; TypeOID: cardinal): string; +{$ENDIF} + end; + + DAChDBIDb = {$IFNDEF NEXTGEN}hDBIDb{$ELSE}TNativeConnect{$ENDIF}; + + {Postgres Engine} + TPSQLEngine = Class(TBaseObject) + private + FDatabase : DAChDBIDb; + FNativeStatus : Integer; + FNativeMsg : string; + function GetDatabase: DAChDBIDb; + procedure SetDatabase(H : DAChDBIDb); + public + constructor Create(P : TObject; Container : TContainer); + Destructor Destroy; Override; + property Status: Integer Read FNativeStatus; + property MessageStatus : String read FNativeMsg; + property Database: DAChDBIDb Read GetDatabase Write SetDatabase; + function IsSqlBased(hDb: DAChDBIDb): Boolean; + function Ping(Params: TStrings; var PingResult: TPingStatus): DBIResult; + function OpenDatabase(Params : TStrings; UseSinleLineConnInfo: boolean; var hDb: DAChDBIDb): DBIResult; + function CloseDatabase(var hDb : DAChDBIDb) : DBIResult; + function OpenTable(hDb: DAChDBIDb; pszTableName: string; pszIndexName: string; iIndexId: Word; eOpenMode: DBIOpenMode; + eShareMode: DBIShareMode; var hCursor: hDBICur; AnOptions: TPSQLDatasetOptions; + Limit, Offset : Integer): DBIResult; + function OpenStoredProcParams(hDb: DAChDBIDb;pszPName: string; ProcOID:cardinal; + List : TList{$IFDEF NEXTGEN}{$ENDIF}): DBIResult; + function OpenStoredProcList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; + function OpenTableList(hDb: DAChDBIDb; pszWild: string; SystemTables: Boolean; List : TStrings): DBIResult; + function OpenUserList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; + function OpenSchemaList(hDb: DAChDBIDb; pszWild: string; SystemSchemas: Boolean; List : TStrings): DBIResult; + function OpenTablespaceList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; + function SetToBookMark(hCur: hDBICur; pBookMark : Pointer) : DBIResult; + function CompareBookMarks(hCur: hDBICur; pBookMark1, pBookMark2 : Pointer;var CmpBkmkResult : CmpBkmkRslt): DBIResult; + function GetNextRecord(hCursor: hDBICur;eLock: DBILockType;pRecBuff: Pointer;pRecProps: pRECProps): DBIResult; + function CloseCursor(hCursor: hDBICur): DBIResult; + function PutField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pSrc: Pointer): DBIResult; + function OpenBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode): DBIResult; + function GetBlobSize(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;var iSize: integer): DBIResult; + function GetBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint;pDest: Pointer;var iRead: integer): DBIResult; + function PutBlob(hCursor : hDBICur; PRecord : Pointer; FieldNo : Word; iOffSet : Longint; iLen : Longint; pSrc : Pointer): DBIResult; + function TruncateBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iLen: Longint): DBIResult; + function FreeBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word): DBIResult; + function CloseBlob(hCursor: hDBICur; FieldNo: Word): DBIResult; + function BeginTran(hDb: DAChDBIDb; eXIL: eXILType; var hXact: hDBIXact): DBIResult; + function EndTran(hDb: DAChDBIDb; hXact: hDBIXact; eEnd : eXEnd): DBIResult; + function GetTranInfo(hDb: DAChDBIDb;hXact: hDBIXact; pxInfo: pXInfo): DBIResult; + function GetTranStatus(hDb: DAChDBIDb; var TranStatus: TTransactionStatusType): DBIResult; + function GetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Pointer;iMaxLen: integer; var iLen: integer): DBIResult; + function SetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Longint): DBIResult; + function GetVchkDesc(hCursor: hDBICur;iValSeqNo: Word; var pvalDesc: VCHKDesc): DBIResult; + function GetCursorProps(hCursor: hDBICur;var curProps: CURProps): DBIResult; + function GetFieldDescs(hCursor: hDBICur; var pfldDesc: TFLDDescList): DBIResult; + function SetToBegin(hCursor: hDBICur): DBIResult; + function SetToEnd(hCursor: hDBICur): DBIResult; + function RelRecordLock(hCursor: hDBICur;bAll: Boolean): DBIResult; + function ReadBlock(hCursor: hDBICur; var iRecords: Integer; pBuf: Pointer): DBIResult; + function InitRecord(hCursor: hDBICur;PRecord: Pointer ): DBIResult; + function InsertRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer): DBIResult; + function AppendRecord(hCursor: hDBICur;PRecord:Pointer): DBIResult; + function ModifyRecord(hCursor: hDBICur;OldRecord,PRecord:Pointer;bFreeLock: Boolean;ARecno: LongInt): DBIResult; + function DeleteRecord(hCursor: hDBICur;PRecord:Pointer): DBIResult; + function SettoSeqNo(hCursor: hDBICur;iSeqNo: Longint): DBIResult; + function GetPriorRecord(hCursor: hDBICur;eLock:DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; + function GetRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; + function GetBookMark(hCur: hDBICur;pBookMark: Pointer): DBIResult; + function GetRecordCount(hCursor: hDBICur;Var iRecCount: integer): DBIResult; + function ForceReread(hCursor: hDBICur): DBIResult; + function GetField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pDest: Pointer;var bBlank: Boolean): DBIResult; + function AddFilter(hCursor: hDBICur;iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr;pfFilter: pfGENFilter;var hFilter: hDBIFilter): DBIResult; + function DropFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; + function ActivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; + function DeactivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; + function GetErrorString(rslt: DBIResult; var ErrorMsg: String): DBIResult; + function QExecDirect(hDb: DAChDBIDb; pszQuery: String;phCur: phDBICur; var AffectedRows : integer): DBIResult; + function QAlloc(hDb: DAChDBIDb;var hStmt: hDBIStmt): DBIResult; + function QPrepare(hStmt: hDBIStmt; pszQuery: String): DBIResult; + function QExec(hStmt: hDBIStmt; phCur: phDBICur; var AffectedRows: integer): DBIResult; + function QFree(var hStmt: hDBIStmt): DBIResult; + function QPrepareProc (hDb: DAChDBIDb; pszProc: PChar; hParams: pointer; var hStmt: hDBIStmt): DBIResult; + function QSetProcParams (hStmt: hDBIStmt; Params: TParams): DBIResult; + function QGetProcParams (hStmt: hDBIStmt; Params: TParams): DBIResult; + function QuerySetParams(hStmt: hDBIStmt;Params : TParams; SQLText : String): DBIResult; + function CheckError : DBIResult; + function GetDatabases(hDb: DAChDBIDb; pszWild: string; List : TStrings):DBIResult; + function GetCharacterSet(hDb : DAChDBIDb; var CharSet : string):DBIResult; + function GetCharacterSets(hDb : DAChDBIDb; List: TStrings):DBIResult; + function SetCharacterSet(hDb : DAChDBIDb; var CharSet : string): DBIResult; + function SetErrorVerbosity(hDb : DAChDBIDb; const ErrorVerbosity: TErrorVerbosity): DBIResult; + function GetCommandTimeout(hDb : DAChDBIDb; var Timeout : cardinal): DBIResult; + function SetCommandTimeout(hDb : DAChDBIDb; var Timeout : cardinal): DBIResult; + function OpenFieldList(hDb: DAChDBIDb; pszTableName: string; pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur): DBIResult; + function OpenIndexList(hDb: DAChDBIDb; pszTableName: string; pszDriverType: string; var hCur: hDBICur): DBIResult; + function EmptyTable(hDb: DAChDBIDb; hCursor : hDBICur; pszTableName : string; pszDriverType : string): DBIResult; + function SetRange(hCursor : hDBICur;bKeyItself: Boolean;iFields1: Word;iLen1: Word;pKey1: Pointer;bKey1Incl: Boolean; + iFields2: Word;iLen2: Word;pKey2: Pointer;bKey2Incl: Boolean): DBIResult; + function ResetRange(hCursor : hDBICur) : DBIResult; + function SwitchToIndex(hCursor : hDBICur; pszIndexName, pszTagName : string; iIndexId : Word; bCurrRec : Boolean) : DBIResult; + function ExtractKey(hCursor: hDBICur;PRecord: Pointer;pKeyBuf: Pointer): DBIResult; + function GetRecordForKey(hCursor: hDBICur; bDirectKey: Boolean; iFields: integer; iLen: integer; pKey: Pointer; pRecBuff: Pointer; AStrictConformity: boolean = False): DBIResult; + function AddIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;var IdxDesc: IDXDesc;pszKeyviolName: string): DBIResult; + function DeleteIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;pszIndexName: string;pszIndexTagName: string;iIndexId: Word): DBIResult; + function GetIndexDesc(hCursor: hDBICur;iIndexSeqNo: Word;var idxDesc: IDXDesc): DBIResult; + function GetIndexDescs(hCursor: hDBICur; idxDescs: TIDXDescList): DBIResult; + function TranslateRecordStructure(pszSrcDriverType: PChar; iFlds: Word; pfldsSrc: pFLDDesc; pszDstDriverType: PChar; pszLangDriver: PChar;pfldsDst: pFLDDesc; bCreatable: Boolean): DBIResult; + function AcqTableLock(hCursor: hDBICur; eLockType: word; bNoWait: boolean): DBIResult; + function SetToKey(hCursor: hDBICur;eSearchCond: DBISearchCond;bDirectKey: Boolean;iFields: integer;iLen: integer;pBuff: Pointer): DBIResult; + function CloneCursor(hCurSrc: hDBICur;bReadOnly: Boolean;bUniDirectional: Boolean;var hCurNew: hDBICur): DBIResult; + function SetToCursor(hDest, hSrc : hDBICur) : DBIResult; + function OpenPGNotify(hDb: DAChDBIDb; var hNotify: hDBIObj): DBIResult; + function ClosePGNotify(var hNotify : hDBIObj) : DBIResult; + function ListenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; + function UnlistenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; + function DoNotify(hNotify : hDBIObj; pszEvent: string) : DBIResult; + function DoNotifyEx(hNotify : hDBIObj; pszChannel: string; pszPayload: string) : DBIResult; + function CheckEvents(hNotify : hDBIObj; var Pid : Integer; var pszOutPut, pszPayload : String) : DBIResult; + function GetBackendPID(hDb: DAChDBIDb; var PID: Integer): DBIResult; + function GetServerVersion(hDb: DAChDBIDb; var ServerVersion: string): DBIResult; + function GetUserProps(hDb: DAChDBIDb; const UserName: string; + var SuperUser, CanCreateDB, CanUpdateSysCatalogs: boolean; + var UserID: integer; var ValidUntil: string):DBIResult; + function GetDBProps(hDB: DAChDBIDb; const DB: string; + var Owner, Tablespace: string; + var IsTemplate: boolean; + var DBOid: cardinal; var Comment: string):DBIResult; + function GetTableProps(hDB: DAChDBIDb; const TableName: string; var Owner, + Comment, Tablespace: string; var TableOid: cardinal):DBIResult; + function GetFieldOldValue(hCursor: hDBICur; AFieldName: string; AParam: TParam): DBIResult; + function GetFieldValueFromBuffer(hCursor: hDBICur; PRecord: Pointer; AFieldName: string; AParam: TParam; const UnchangedAsNull: boolean): DBIResult; + function GetLastInsertId(hCursor: hDBICur; const FieldNum: integer; var ID: integer): DBIResult; + function GetFieldTypeOID(hCursor: hDBICur; const FieldNum: integer): cardinal; + function GetFieldOrigin(hCursor: hDBICur; const FieldNum: integer): string; + + function CheckBuffer(hCursor: hDBICur; PRecord: Pointer): DBIResult; + function Reset(hDb: DAChDBIDb): DBIResult; + function CancelBackend(hDb: DAChDBIDb; PID: Integer): DBIResult; + function SelectStringDirect(hDb: DAChDBIDb; + pszQuery : PChar; + var IsOk : boolean; + var aResult : string; + aFieldNumber : integer):DBIResult;overload; + function SelectStringDirect(hDb: DAChDBIDb; + pszQuery : PChar; + var IsOk : boolean; + var aResult : string; + aFieldName : string):DBIResult;overload; + function SelectStringsDirect(hDb: DAChDBIDb; + pszQuery : PChar; + aList : TStrings; + aFieldNumber : integer):DBIResult;overload; + function SelectStringsDirect(hDb: DAChDBIDb; + pszQuery : PChar; + aList : TStrings; + aFieldName : string):DBIResult;overload; + + end; + + ///////////////////////////////////////////////////////// + // Forward declaration // + ///////////////////////////////////////////////////////// + TNativeDataSet = Class; + ////////////////////////////////////////////////////////// + //Class : TPSQLField + //Description : PSQL Field Description + ////////////////////////////////////////////////////////// + + + + TPSQLField = Class(TCollectionItem) + private + FDesc : FldDesc; + FValCheck : VCHKDesc; + FBuffer : Pointer; + FData : Pointer; + FStatus : PFieldStatus; + FArray : Boolean; + FNativeBLOBType: TNativeBLOBType; + function GetLocalSize : integer; + procedure SetLocalSize(S : integer); + function GetLocalType : integer; + procedure SetLocalType(S : integer); + function GetFieldName : String; + procedure SetFieldName(Const Value : String); + procedure SetBuffer(PRecord : Pointer); + function GetChanged : Boolean; + procedure SetChanged(Flag : Boolean); + function GetNull : Boolean; + procedure SetNull(Flag : Boolean); + function GetFieldDefault : string;//mi + procedure SetFieldDefault(aStr : string); + function GetNativeDataset: TNativeDataSet; + function GetNativeConnect: TNativeConnect; + public + constructor CreateField(Owner : TCollection; P : FldDesc; P1 :VCHKDesc; FNum, LType, LSize : integer; isArray : Boolean); + + function FieldValue: PAnsiDACChar; + function FieldValueAsStr: string; //this will be used in SQLs; + + property Buffer : Pointer Read FBuffer Write SetBuffer; + property Data : Pointer Read FData; + property DataOffset : integer Read FDesc.iOffset Write FDesc.iOffset; + property Description : FLDDesc Read FDesc Write FDesc; + property ValCheck : VCHKDesc Read FValCheck Write FValCheck; + property FieldChanged : Boolean Read GetChanged Write SetChanged; + property FieldNull : Boolean Read GetNull Write SetNull; + property FieldStatus : PFieldStatus Read FStatus; + property NullOffset : integer Read FDesc.iNullOffset Write FDesc.iNullOffset; + property FieldNumber : integer Read FDesc.iFldNum Write FDesc.iFldNum; + property FieldName : String Read GetFieldName Write SetFieldName; + property FieldType : integer Read FDesc.iFldType Write FDesc.iFldType; + property FieldSubType : integer Read FDesc.iSubType Write FDesc.iSubType; + property FieldUnits1 : integer Read FDesc.iUnits1 Write FDesc.iUnits1; + property FieldUnits2 : integer Read FDesc.iUnits2 Write FDesc.iUnits2; + property FieldLength : integer Read FDesc.iLen Write FDesc.iLen; + property FieldDefault: string read GetFieldDefault write SetFieldDefault;//mi + property NativeType : integer Read GetLocalType Write SetLocalType; + property NativeSize : integer Read GetLocalSize Write SetLocalSize; + property FieldArray : Boolean Read FArray write FArray; + property NativeBLOBType: TNativeBLOBType read FNativeBLOBType + write FNativeBlobType; + property NativeDataset : TNativeDataSet read GetNativeDataset; + property NativeConnect : TNativeConnect read GetNativeConnect; + end; + + ////////////////////////////////////////////////////////// + //Class : TPSQLFields + //Description : List PSQL Fields for current cursor + ////////////////////////////////////////////////////////// + TPSQLFields = Class(TCollection) + private + FTable : TNativeDataSet; + function GetField(Index : Integer) : TPSQLField; + function GetNativeConnect: TNativeConnect; + public + constructor Create(Table : TNativeDataSet); + function AddField(P : FldDesc; P1 :VCHKDesc; FNum, LType, LSize : integer; isArray : Boolean): TPSQLField; + property Field[Index : Integer] : TPSQLField Read GetField; Default; + procedure SetFields(PRecord : Pointer); + function FieldNumberFromName(SearchName : PChar) : Integer; + + property NativeDataset : TNativeDataSet read FTable; + property NativeConnect : TNativeConnect read GetNativeConnect; + end; + + ////////////////////////////////////////////////////////// + //Class : TPSQLIndex + //Description : PSQL Index Description + ////////////////////////////////////////////////////////// + TPSQLIndex = Class(TCollectionItem) + private + FDesc : IDXDesc; + public + constructor CreateIndex(Owner : TCollection; P : pIDXDesc); + destructor Destroy; override; + property Description : IDXDesc Read FDesc Write FDesc; + property IndexNumber : integer Read FDesc.iIndexID Write FDesc.iIndexID; + property IndexName : string Read FDesc.szName Write FDesc.szName; + property Primary : WordBool Read FDesc.bPrimary Write FDesc.bPrimary; + property Unique : WordBool Read FDesc.bUnique Write FDesc.bUnique; + property Descending : WordBool Read FDesc.bDescending Write FDesc.bDescending; + property FldsInKey : integer Read FDesc.iFldsInKey Write FDesc.iFldsInKey; + property KeyLen : integer Read FDesc.iKeyLen Write FDesc.iKeyLen; + property BlockSize : integer Read FDesc.iBlockSize Write FDesc.iBlockSize; + end; + + ////////////////////////////////////////////////////////// + //Class : TPSQLIndexes + //Description : List PSQL Indexes for current cursor + ////////////////////////////////////////////////////////// + TPSQLIndexes = Class(TCollection) + private + FTable : TNativeDataSet; + FUpdated: boolean; + function GetIndex(Index : Integer) : TPSQLIndex; + function FindByName(Name :String): TPSQLIndex; + procedure SetNeedUpdate(const Value: boolean); + Public + constructor Create(Table : TNativeDataSet); + property mIndex[Index : Integer] : TPSQLIndex Read GetIndex; Default; + function SetIndex(Name,Fields : String;aPrimary,aUnique,aDesc : Boolean): integer; + function FieldNumberFromName(SearchName : PChar) : Integer; + property Updated: boolean read FUpdated write SetNeedUpdate; + end; + + ////////////////////////////////////////////////////////// + //Class : TPSQLFilter + //Description : Filtered object + ////////////////////////////////////////////////////////// + TPSQLFilter = class(TObject) + protected + function PerformCANOp(AOperator : CANOp; AOp1, AOp2 : Variant) : Variant; + function PerformCanConst(ANode : PCANConst; ValuesStart : Pointer; Var FldType : TFldType) : Variant; + function TimeOf(const ADateTime: TDateTime): TDateTime; + private + FDataSet : TNativeDataSet; + FExpression : pCANExpr; + FActive : Boolean; + FExprSize : Word; + FRecBuff : Pointer; + FPfFilter : pfGENFilter; + FClientData : Longint; + function GetNodeStart : Integer; + function GetLiteralPtr(AOffset: Word):Pointer; + function GetNodeByOffset(AOffSet : Integer) : PCanNode; + function UnaryNode(ANode : PCANUnary) : Variant; + function BinaryNode(ANode : PCANBinary) : Variant; + function CompareNode(ANode : PCANCompare) : Variant; + function FieldNode(ANode : pCANField) : Variant; + function GetNodeValue(AOffSet : Integer) : Variant; + function CalcExpression(ANode : PCanNode) : Variant; + function ListOfValues(ANode : pCANListElem): Variant; + function PerformLikeCompare(Const Value, Mask : String; CaseSen : Boolean) : Boolean; + function PerformInCompare(AOp1, AOp2 : Variant) : Boolean; + property NodeStart : Integer Read GetNodeStart; + public + constructor Create(Owner : TNativeDataSet; AClientData : Longint; Exp : pCANExpr; pfFilt : pfGENFilter); + Destructor Destroy; Override; + function GetFilterResult(PRecord : Pointer) : Variant; + property Active : Boolean Read FActive Write FActive; + end; + + ////////////////////////////////////////////////////////// + //Class : TPSQLNative + //Description : PSQL Native Field Description + ////////////////////////////////////////////////////////// + TPSQLNative = Class(TCollectionItem) + private + FDesc : TPGField_Info; + Public + constructor CreateNative(Owner : TCollection; P : PPGField_Info); + property Description : TPGField_Info Read FDesc Write FDesc; + Published + property NativeNumber : Integer Read FDesc.FieldIndex Write FDesc.FieldIndex; + property NativeName : String Read FDesc.FieldName Write FDesc.FieldName; + property NativeType : cardinal Read FDesc.FieldType Write FDesc.FieldType; + property NativeSize : Integer Read FDesc.FieldSize Write FDesc.FieldSize; + property NativeMaxSize : Integer Read FDesc.FieldMaxSize Write FDesc.FieldMaxSize; + property NativeDefault : String Read FDesc.FieldDefault Write FDesc.FieldDefault; + property NativeNotNull : boolean read FDesc.FieldNotNull write FDesc.FieldNotNull; + property NativeTypMod : Integer read FDesc.FieldTypMod write FDesc.FieldTypMod; + end; + + ////////////////////////////////////////////////////////// + //Class : TPSQLNatives + //Description : List PSQL Native Fields for current cursor + ////////////////////////////////////////////////////////// + TPSQLNatives = Class(TCollection) + private + FTable : TNativeDataSet; + function GetNative(Index : Integer) : TPSQLNative; + Public + constructor Create(Table : TNativeDataSet); + property Field_Info[Index : Integer] : TPSQLNative Read GetNative; Default; + procedure SetNative(aIndex : Integer; aName : String; aType, aSize, aMaxSize, aTypMod : Integer); + end; + + ////////////////////////////////////////////////////////// + //Class : TNativeDataSet + //Description : Base class for All Objects + ////////////////////////////////////////////////////////// + TNativeDataSet = Class(TObject) + private + RecNo : LongInt; {Record Nomber} + FOMode : DBIOpenMode; {Open mode} + FStatement : PPGresult; {Handle PSQL Cursor } + FFilters : TContainer; {Filters list} + FFilterActive : Boolean; {is Active filter for Query } + FReFetch : Boolean; {Batch Insert allows} + FFetched : boolean; // if dsoFetchOnDemand shows if all rows are consumed + FFieldDescs : TPSQLFields; + FIndexDescs : TPSQLIndexes; + FNativeDescs : TPSQLNatives; {Native field Description} + FLastOperationTime: cardinal; + FKeyNumber : SmallInt; + FIndexName : string; + FPrimaryKeyNumber: SmallInt; + FPreventRememberBuffer : boolean; //prevent record buffer storing while reading BLOB field data + FGetKeyDesc : Boolean; + FKeyDesc : IDXDesc; + Ranges : Boolean; + FRecSize : Integer; + FConnect : TNativeConnect; + FOpen : Boolean; + FAffectedRows : Integer; + FBookOfs : Integer; + FRecordState : TRecordState; + FLastDir : TDir; + FCurrentBuffer : Pointer; + FInternalBuffer : Pointer; + FIsLocked : Boolean; + FReRead : Boolean; + OrderClause : TStrings; + RangeClause : TStrings; + StandartClause : TStrings; + LimitClause : TStrings; + AutoReExec : Boolean; + FLimit : Integer; + FOffset : Integer; + MasterCursor : Pointer; + FBlobHandle : cardinal; + FlocalBHandle : Integer; + FBlobOpen : Boolean; + FSystemNeed : Boolean; + FFieldMinSizes : array of integer; //to decrease FieldMinSize routine access + FFieldTypType : DACAString; //to store pg_type.typtype + FSortingIndex : array of integer; //filled with SortBy method + FSortingFields : string; //"fieldname" ASC|DESC, ... + FOptions : TPSQLDatasetOptions; + FCustomCompareFunc: TPSQLDatasetSortCompare; + procedure SetInternalBuffer(Buffer : Pointer); + function GetInternalBuffer: Pointer; + function GetCurrentBuffer: Pointer; + procedure SetCurrentBuffer(PRecord : Pointer); + procedure SetBufferAddress(P : Pointer); + procedure SetKeyNumber(newValue: SmallInt); + function FieldOffset(iField: Integer): integer; + function GetBookMarkSize: Integer; + function GetIndexCount: Integer; + procedure SetBufBookmark; + procedure SetRecordNumber(RecNom : Longint); + function GetRecordNumber : Longint; + function GetRecCount: LongInt; + procedure InitFieldDescs; + procedure CheckFilter(PRecord : Pointer); + procedure FirstRecord; virtual; + procedure LastRecord; + procedure NextRecord(); + procedure PrevRecord(); + procedure CurrentRecord(ARecNo : LongInt); + procedure GetWorkRecord(eLock : DBILockType; PRecord : Pointer); + procedure LockRecord(eLock : DBILockType); + function FilteredRecord(PRecord : Pointer) : Boolean; + function FetchRecords(const NumberOfRecs: integer = 1): integer; + procedure UpdateFilterStatus; + function FieldCount : Integer; + procedure InternalSortBy(const Fields: array of Integer; const IsReverseOrder : array of boolean); + function GetRecNo: integer; + procedure InternalReadBuffer; + function GetTableName: string; + procedure SetTableName(Name : string); + function CheckUniqueKey(var KeyNumber : integer): Boolean; + procedure GetKeys(Unique: Boolean;var FieldList: TFieldArray; var FieldCount: Integer); + function GetLOUnlinkSQL(ObjOID: string): string; overload; + function GetDeleteSQL(Table: string; PRecord: Pointer): string; + function GetInsertSQL(Table: string; PRecord: Pointer; ReturnUpdated: boolean = False): string; + function GetUpdateSQL(Table: string; OldRecord, PRecord: Pointer; ReturnUpdated: boolean = False): String; + function FieldVal(FieldNo: Integer; FieldPtr : Pointer):String; + ////////////////////////////////////////////////////////// + // PSQL FIELD PARAMS // + ////////////////////////////////////////////////////////// + function FieldName(FieldNum: Integer): String; + function FieldIndex(FieldName: String): Integer; + function FieldSize(FieldNum: Integer): Integer; + function FieldMaxSize(FieldNum: Integer): Integer; + function FieldMaxSizeInBytes(FieldNum: Integer): Integer; + function FieldMinSize(FieldNum: Integer): Integer; + function FieldType(FieldNum: Integer): cardinal; + function FieldTypMod(FieldNum: Integer): Integer; + function FieldTable(FieldNum: integer): cardinal; + function FieldOrigin(FieldNum: integer): string; + function FieldPosInTable(FieldNum: integer): Integer; + function FieldIsNull(FieldNum: Integer): Boolean; + function Field(FieldNum: Integer): string; + function FieldBuffer(FieldNum: Integer): PAnsiDACChar; +// function FieldByName(FieldName: String): string; + function GetSQLClause: string; + function GetBufferSize : integer; Virtual; + function GetWorkBufferSize : integer; virtual; + procedure GetNativeDesc(FieldNo : Integer; var P : FldDesc; var P1: VCHKDesc; Var LocType, LocSize : Integer; var LocArray: Boolean); + procedure NativeToDelphi(P: TPSQLField; PRecord: Pointer; pDest: Pointer; var bBlank: Boolean); + procedure DelphiToNative(P: TPSQLField; PRecord: Pointer;pSrc: Pointer); + procedure CheckParam(Exp : Boolean;BDECODE : Word); + function GetRecordSize: Integer; + function GetFieldInfo(Index : Integer) : TPGFIELD_INFO; + procedure ReOpenTable; + procedure ClearIndexInfo; + function GetFieldTypType(Index: integer): AnsiDACChar; + private + FTableName: string; + property KeyNumber: SmallInt Read FKeyNumber Write SetKeyNumber; + property RecordCount : LongInt Read GetRecCount; + property Fields : TPSQLFields Read FFieldDescs; + property RecordSize : Integer read GetRecordSize; + property FieldInfo[Index: Integer]:TPGFIELD_INFO Read GetFieldInfo; + property BookMarkSize : Integer Read GetBookMarkSize; + property BufferAddress : Pointer Write SetBufferAddress; + property CurrentBuffer : Pointer Read GetCurrentBuffer Write SetCurrentBuffer; + property InternalBuffer : Pointer Read GetInternalBuffer Write SetInternalBuffer; + property IndexCount : Integer Read GetIndexCount; + //insert, update, delete stuff + function UuidValue(P : Pointer; NeedQuote: boolean = True): string; + function StrValue(P : Pointer; NeedQuote: boolean = True): string; + function MemoValue(P : Pointer; NeedQuote: boolean = True): string; + function BlobValue(P : Pointer; Fld: TPSQLField; NeedEscape: boolean = True): string; overload; + function BlobValue(MS: TStream; isBytea: boolean; NeedEscape: Boolean = True): string; overload; + procedure ReadBlock(var iRecords: Integer; pBuf: Pointer); + public + SQLQuery : String; + ROWID : OID; + isQuery : boolean; + constructor Create(PSQL : TNativeConnect; + //Container : TContainer; + AnOptions: TPSQLDatasetOptions; + AName, IndexName : string; + Index : Word; + Limit, Offset : Integer; + ASystem: Boolean = False); + Destructor Destroy; Override; + procedure CompareBookMarks(pBookMark1, pBookMark2 : Pointer; var CmpBkmkResult : CmpBkmkRslt); + procedure GetBookMark(P : Pointer); + function GetLastInsertID(const KeyNumber: integer):integer; + procedure Execute; + procedure OpenTable; + procedure GetField(FieldNo : Word; PRecord : Pointer; pDest : Pointer; var bBlank : Boolean); + procedure PutField(FieldNo: Word;PRecord : Pointer; PSrc:Pointer); + procedure CloseTable; + procedure GetVchkDesc(iValSeqNo: Word; var pvalDesc: VCHKDesc); + procedure GetCursorProps(var curProps : CURProps); + procedure GetFieldDescs(var pFDesc : TFLDDescList); + procedure GetRecordCount(var iRecCount : integer); virtual; + procedure GetNextRecord(eLock : DBILockType; PRecord : Pointer; pRecProps : pRECProps); Virtual; + procedure SetToRecord(RecNo : LongInt); + procedure SetToBookmark(P : Pointer); virtual; + procedure GetRecord(eLock : DBILockType; PRecord : Pointer; pRecProps : pRECProps); + procedure GetPriorRecord(eLock : DBILockType; PRecord : Pointer; pRecProps : pRECProps); + procedure AddFilter(iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr;pfFilter: pfGENFilter; var hFilter : hDBIFilter); + procedure DropFilter(hFilter: hDBIFilter); + procedure ActivateFilter(hFilter : hDBIFilter); + procedure DeactivateFilter(hFilter : hDBIFilter); + procedure GetProp(iProp: integer;PropValue: Pointer; iMaxLen: integer; var iLen: integer); + procedure SetProp(iProp: integer; PropValue : Longint); + procedure SetToBegin; Virtual; + procedure SetToEnd; + procedure ForceReread; + procedure InitRecord(PRecord : Pointer); + procedure InsertRecord(eLock : DBILockType; PRecord : Pointer); + procedure AppendRecord(PRecord : Pointer); + procedure ModifyRecord(OldRecord,PRecord : Pointer; bFreeLock : Boolean;ARecNo : LongInt); + procedure DeleteRecord(PRecord : Pointer); + //-->blob stuff + procedure OpenBlob(PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode); + procedure FreeBlob(PRecord: Pointer;FieldNo: Word); + procedure CloseBlob(FieldNo: Word); + procedure GetBlobSize(PRecord : Pointer; FieldNo : Word; var iSize : integer); + procedure GetBlob(PRecord : Pointer; FieldNo : Word; iOffSet : Longint; iLen : Longint; pDest : Pointer; var iRead : integer); + procedure PutBlob(PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint; pSrc : Pointer); + procedure TruncateBlob(PRecord : Pointer; FieldNo : Word; iLen : Longint); + procedure FreeBlobStreams(PRecord: Pointer); + //<--blob stuff + procedure QuerySetParams(Params : TParams; SQLText : String); + procedure StoredProcSetParams(Params: TParams); + procedure StoredProcGetParams(Params: TParams); + procedure RelRecordLock(bAll: Boolean); + procedure ExtractKey(PRecord: Pointer;pKeyBuf: Pointer); + procedure GetRecordForKey(bDirectKey: Boolean; iFields: integer; iLen: integer; pKey: Pointer; pRecBuff: Pointer; AStrictConformity: boolean = False); + function findrows(const Fields: array of Integer; const SearchFields:array of String; ACaseSen : Boolean; APartLen : Integer; AStrictConformity: boolean = False):int64; + function SetRowPosition(iFields : Integer; LID : Int64; pRecBuffer : Pointer):Boolean; + procedure GetIndexDesc(iIndexSeqNo : Word; var idxDesc : IDXDesc); + procedure GetIndexDescs(Descs : TIDXDescList); + procedure SetRange(bKeyItself : Boolean; iFields1 : Word; iLen1 : Word; pKey1 : Pointer; + bKey1Incl : Boolean; iFields2 : Word; iLen2 : Word; pKey2 : Pointer; bKey2Incl : Boolean); + procedure ResetRange; + procedure SwitchToIndex(pszIndexName : string; pszTagName : string; iIndexId : Word; bCurrRec : Boolean); + procedure SettoSeqNo(iSeqNo: Longint); + procedure EmptyTable; + procedure AddIndex(var IdxDesc: IDXDesc; pszKeyviolName : string); + procedure DeleteIndex(pszIndexName: string; pszIndexTagName: string; iIndexId: Word); + procedure AcqTableLock(eLockType: word; bNoWait: boolean); + procedure SetToKey(eSearchCond: DBISearchCond; bDirectKey: Boolean;iFields: Word;iLen: Word;pBuff: Pointer); + procedure Clone(bReadOnly: Boolean;bUniDirectional: Boolean;var hCurNew: hDBICur); + procedure SetToCursor(hDest : hDBICur); + + property RecordNumber : LongInt Read GetRecordNumber Write SetRecordNumber; + property RecordState: TRecordState Read FRecordState Write FRecordState; + property TableName : string Read GetTableName Write SetTableName; + + property Options: TPSQLDatasetOptions read FOptions write FOptions; + + property FieldTypTypes[Index: integer]: AnsiDACChar read GetFieldTypType; + + procedure FieldOldValue(AFieldName: string; var AParam: TParam); + procedure FieldValueFromBuffer(PRecord: Pointer; AFieldName: string; var AParam: TParam; const UnchangedAsNull: boolean); + + property IsLocked: boolean read FIsLocked write FIsLocked; + property LastOperationTime: cardinal read FLastOperationTime; + function CheckCanLive: boolean; //pasha_golub 14.07.06 + function HasFieldTimeZone(const FldNum: integer):boolean; + procedure SortBy(FieldNames: string); overload; + procedure SortBY(FieldNames: string; Compare: TPSQLDatasetSortCompare); overload; + function IsSortedLocally: boolean; + property PreventRememberBuffer : boolean read FPreventRememberBuffer write FPreventRememberBuffer; + property Connect: TNativeConnect read FConnect; +end; + + TIndexList = Class(TNativeDataSet) + private + Descs : TIDXDescList; + Items : integer; + Position : integer; + Public + constructor Create(PSQL : TNativeConnect; D : TIDXDescList; TotalCount : integer); + destructor Destroy; Override; + procedure SetToBegin; Override; + procedure GetNextRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); override; + procedure GetIdxDesc(Precord: PIdxDesc); + function GetBufferSize : integer; Override; + function GetWorkBufferSize : integer; Override; + procedure SetToBookmark(P : Pointer); override; + procedure GetRecordCount(Var iRecCount : integer); override; + end; + + TFieldList = Class(TNativeDataSet) + private + Descs : TFLDDescList; + Items : integer; + Position : integer; + public + constructor Create(PSQL : TNativeConnect; D : TFLDDescList; TotalCount : integer); + destructor Destroy; Override; + procedure SetToBegin; Override; + function GetBufferSize : integer; Override; + procedure GetNextRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); override; + procedure GetFLDDesc(PRecord: pFLDDesc); + function GetWorkBufferSize : integer; Override; + procedure SetToBookmark(P : Pointer); override; + procedure GetRecordCount(Var iRecCount : integer); override; + end; + + TNativePGNotify = class + protected + FConnect : TNativeConnect; + FHandle : PPGnotify; + procedure InternalExecute(Sql: string); + public + constructor Create(AConnect: TNativeConnect); + destructor Destroy; override; + procedure ListenTo(Event: string); + procedure UnlistenTo(Event: string); + procedure DoNotify(Event: string); + procedure DoNotifyEx(Channel: string; Payload: string); + function CheckEvents(var PID : Integer; var Payload: string): string; + property Handle: PPGnotify read fHandle; + end; + +function AdjustNativeField(iField : TPSQLField; Src, Dest : Pointer; Var Blank : Boolean) : Word; +function AdjustDelphiField(iField : TPSQLField; Src, Dest : Pointer) : Word; +procedure PSQLException(PSQL : TNativeConnect); +procedure PSQLExceptionMsg(PSQL : TNativeConnect; Const ErrorMsg : String ); + + +function BDETOPSQLStr(Field : TFieldDef): String; +function SQLCreateIdxStr(Index : TPSQLIndex;TableName : String;Flds : TPSQLFields): String; + +function _PQSendQuery(AConnection: TNativeConnect; AQuery: string): integer; +function _PQExecute(AConnection: TNativeConnect; AQuery: string): PPGResult; +function _PQExecuteParams(AConnection: TNativeConnect; AQuery: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; +function _PQexecPrepared(AConnection: TNativeConnect; AStmName: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; + +{$IFDEF M_DEBUG} +function PQExec(Handle: PPGconn; AQuery: PAnsiDACChar): PPGresult; +procedure LogDebugMessage(MsgType, Msg: string); + +var SessionStart: cardinal; +{$ENDIF} + + +{$IFDEF DELPHI_5} +function ifThen(aCondition: boolean; IfTrue: string; IfFalse: string = ''): string; overload; +function ifThen(aCondition: boolean; IfTrue: integer; IfFalse: integer = 0): integer; overload; +{$ENDIF} + +{$IFNDEF DELPHI_15} +{$IFDEF DELPHI_12} +function BcdToStr(const Bcd: TBcd; Format: TFormatSettings): string; +function StrToBcd(const AValue: string; Format: TFormatSettings): TBcd; +{$ENDIF} +{$ENDIF} + +{$IFDEF UNDER_DELPHI_6} +function StrToFloat(const S: string; + const FormatSettings: TFormatSettings): Extended; +function FloatToStr(Value: Extended; + const FormatSettings: TFormatSettings): string; +function FormatDateTime(const Format: string; DateTime: TDateTime; + const FormatSettings: TFormatSettings): string; +procedure DateTimeToString(var Result: string; const Format: string; + DateTime: TDateTime; const FormatSettings: TFormatSettings); +{$ENDIF} + +implementation + +uses PSQLDbTables, PSQLMonitor, + {$IFNDEF DELPHI_5}StrUtils,{$ENDIF} + {$IFDEF MSWINDOWS}Windows,{$ENDIF} + {$IFNDEF FPC}DbConsts,{$ENDIF} + {$IFDEF DELPHI_18}{$IFNDEF NEXTGEN}System.AnsiStrings,{$ENDIF}{$ENDIF} + {$IFDEF NEXTGEN}Character,{$ENDIF} + PSQLExtMask, PSQLFields; + +{**************************************************************************} +{ Utility Objects } +{**************************************************************************} + +{$IFDEF TRIAL} +function PQntuples(Res: PPGresult): Integer; +begin + Result := Min(PSQLTypes.PQntuples(Res), 25); +end; +{$ENDIF} + +{$IFDEF M_DEBUG} +var F: TextFile; + DebugFileOpened: boolean = False; + +procedure LogDebugMessage(MsgType, Msg: string); +begin + if not DebugFileOpened or (Msg = EmptyStr) then Exit; + Msg := StringReplace(Msg, '<','<', [rfReplaceAll]); + Msg := StringReplace(Msg, '>','>', [rfReplaceAll]); + WriteLn(F,'',GetTickCount() - SessionStart,' ms', MsgType, '
',Msg,'
'); +end; + +function PQConnectDB(ConnInfo: PAnsiDACChar): PPGconn; +begin + Result := PSQLTypes.PQConnectDB(ConnInfo); + LogDebugMessage('CONN', String(ConnInfo)); +end; + +function PQExec(Handle: PPGconn; AQuery: PAnsiDACChar): PPGresult; +begin + Result := PSQLTypes.PQexec(Handle,AQuery); + LogDebugMessage('EXEC', String(AQuery)); +end; + +function lo_creat(Handle: PPGconn; mode: Integer): Oid; +begin + Result := PSQLTypes.lo_creat(Handle,mode); + LogDebugMessage('loCr', 'LO OID = '+inttostr(Result)); +end; + +function lo_open(Handle: PPGconn; lobjId: Oid; mode: Integer): Integer; +begin + Result := PSQLTypes.lo_open(Handle,lobjId,mode); + LogDebugMessage('loOp', 'oid = '+inttostr(lobjId)+'; fd = '+inttostr(Result)); +end; + +function lo_close(Handle: PPGconn; fd: Integer): Integer; +begin + Result := PSQLTypes.lo_close(Handle,fd); + LogDebugMessage('loCl', 'fd = '+inttostr(fd)); +end; + +function PQerrorMessage(Handle: PPGconn): PAnsiDACChar; +begin + Result := PSQLTypes.PQerrorMessage(Handle); + LogDebugMessage('ERR ', string(Result)); +end; + +procedure OpenDebugFile; +var Name, Time: string; +begin + SessionStart := GetTickCount(); + DateTimeToString(Time, 'dd.mm.yy_hh.nn.ss', Now(), PSQL_FS); + Name := '_' + Time; + Name := ChangeFileExt(GetModuleName(HInstance), Name + '_log.html'); + AssignFile(F, Name); + {$I-} + if FileExists(Name) then + Append(F) + else + Rewrite(F); + {$I+} + DebugFileOpened := IOResult = 0; + if not DebugFileOpened then Exit; + WriteLn(F,'
',''); + LogDebugMessage('INFO',Format('----- Session started at %s -----', [Time])); +end; + +procedure CloseDebugFile; +begin + if not DebugFileOpened then Exit; + LogDebugMessage('INFO','----- Session closed -----'); + WriteLn(F,'
'); + CloseFile(F); +end; +{$ENDIF} + +{$IFDEF UNDER_DELPHI_6} +const +// 8087 status word masks + mIE = $0001; + mDE = $0002; + mZE = $0004; + mOE = $0008; + mUE = $0010; + mPE = $0020; + mC0 = $0100; + mC1 = $0200; + mC2 = $0400; + mC3 = $4000; +const + // 1E18 as a 64-bit integer + Const1E18Lo = $0A7640000; + Const1E18Hi = $00DE0B6B3; + DCon10: Integer = 10; + +procedure GetLocaleFormatSettings(LCID: Integer; + var FormatSettings: TFormatSettings); +begin + with FormatSettings do + DecimalSeparator := '.'; +end; + +function TextToFloat(Buffer: PChar; var Value; + ValueType: TFloatValue; const FormatSettings: TFormatSettings): Boolean; + +const +// 8087 control word +// Infinity control = 1 Affine +// Rounding Control = 0 Round to nearest or even +// Precision Control = 3 64 bits +// All interrupts masked + CWNear: Word = $133F; + +var + Temp: Integer; + CtrlWord: Word; + DecimalSep: Char; + SaveGOT: Integer; +asm + PUSH EDI + PUSH ESI + PUSH EBX + MOV ESI,EAX + MOV EDI,EDX +{$IFDEF PIC} + PUSH ECX + CALL GetGOT + POP EBX + MOV SaveGOT,EAX +{$ELSE} + MOV SaveGOT,0 + MOV EBX,ECX +{$ENDIF} + MOV EAX,FormatSettings + MOV AL,[EAX].TFormatSettings.DecimalSeparator + MOV DecimalSep,AL + FSTCW CtrlWord + FCLEX +{$IFDEF PIC} + FLDCW [EAX].CWNear +{$ELSE} + FLDCW CWNear +{$ENDIF} + FLDZ + CALL @@SkipBlanks + MOV BH, byte ptr [ESI] + CMP BH,'+' + JE @@1 + CMP BH,'-' + JNE @@2 +@@1: INC ESI +@@2: MOV ECX,ESI + CALL @@GetDigitStr + XOR EDX,EDX + MOV AL,[ESI] + CMP AL,DecimalSep + JNE @@3 + INC ESI + CALL @@GetDigitStr + NEG EDX +@@3: CMP ECX,ESI + JE @@9 + MOV AL, byte ptr [ESI] + AND AL,0DFH + CMP AL,'E' + JNE @@4 + INC ESI + PUSH EDX + CALL @@GetExponent + POP EAX + ADD EDX,EAX +@@4: CALL @@SkipBlanks + CMP BYTE PTR [ESI],0 + JNE @@9 + MOV EAX,EDX + CMP BL,fvCurrency + JNE @@5 + ADD EAX,4 +@@5: PUSH EBX + MOV EBX,SaveGOT + CALL FPower10 + POP EBX + CMP BH,'-' + JNE @@6 + FCHS +@@6: CMP BL,fvExtended + JE @@7 + FISTP QWORD PTR [EDI] + JMP @@8 +@@7: FSTP TBYTE PTR [EDI] +@@8: FSTSW AX + TEST AX,mIE+mOE + JNE @@10 + MOV AL,1 + JMP @@11 +@@9: FSTP ST(0) +@@10: XOR EAX,EAX +@@11: FCLEX + FLDCW CtrlWord + FWAIT + JMP @@Exit + +@@SkipBlanks: + +@@21: LODSB + OR AL,AL + JE @@22 + CMP AL,' ' + JE @@21 +@@22: DEC ESI + RET + +// Process string of digits +// Out EDX = Digit count + +@@GetDigitStr: + + XOR EAX,EAX + XOR EDX,EDX +@@31: LODSB + SUB AL,'0'+10 + ADD AL,10 + JNC @@32 +{$IFDEF PIC} + XCHG SaveGOT,EBX + FIMUL [EBX].DCon10 + XCHG SaveGOT,EBX +{$ELSE} + FIMUL DCon10 +{$ENDIF} + MOV Temp,EAX + FIADD Temp + INC EDX + JMP @@31 +@@32: DEC ESI + RET + +// Get exponent +// Out EDX = Exponent (-4999..4999) + +@@GetExponent: + + XOR EAX,EAX + XOR EDX,EDX + MOV CL, byte ptr [ESI] + CMP CL,'+' + JE @@41 + CMP CL,'-' + JNE @@42 +@@41: INC ESI +@@42: MOV AL, byte ptr [ESI] + SUB AL,'0'+10 + ADD AL,10 + JNC @@43 + INC ESI + IMUL EDX,10 + ADD EDX,EAX + CMP EDX,500 + JB @@42 +@@43: CMP CL,'-' + JNE @@44 + NEG EDX +@@44: RET + +@@Exit: + POP EBX + POP ESI + POP EDI +end; + +procedure PutExponent; +// Store exponent +// In AL = Exponent character ('E' or 'e') +// AH = Positive sign character ('+' or 0) +// BL = Zero indicator +// ECX = Minimum number of digits (0..4) +// EDX = Exponent +// EDI = Destination buffer +asm + PUSH ESI +{$IFDEF PIC} + PUSH EAX + PUSH ECX + CALL GetGOT + MOV ESI,EAX + POP ECX + POP EAX +{$ELSE} + XOR ESI,ESI +{$ENDIF} + STOSB + OR BL,BL + JNE @@0 + XOR EDX,EDX + JMP @@1 +@@0: OR EDX,EDX + JGE @@1 + MOV AL,'-' + NEG EDX + JMP @@2 +@@1: OR AH,AH + JE @@3 + MOV AL,AH +@@2: STOSB +@@3: XCHG EAX,EDX + PUSH EAX + MOV EBX,ESP +@@4: XOR EDX,EDX + DIV [ESI].DCon10 + ADD DL,'0' + MOV [EBX],DL + INC EBX + DEC ECX + OR EAX,EAX + JNE @@4 + OR ECX,ECX + JG @@4 +@@5: DEC EBX + MOV AL,[EBX] + STOSB + CMP EBX,ESP + JNE @@5 + POP EAX + POP ESI +end; + + +function FloatToText(BufferArg: PChar; const Value; ValueType: TFloatValue; + Format: TFloatFormat; Precision, Digits: Integer; + const FormatSettings: TFormatSettings): Integer; +var + Buffer: Cardinal; + FloatRec: TFloatRec; + SaveGOT: Integer; + DecimalSep: Char; + ThousandSep: Char; + CurrencyStr: Pointer; + CurrFmt: Byte; + NegCurrFmt: Byte; +asm + PUSH EDI + PUSH ESI + PUSH EBX + MOV Buffer,EAX +{$IFDEF PIC} + PUSH ECX + CALL GetGOT + MOV SaveGOT,EAX + POP ECX +{$ENDIF} + MOV EAX,FormatSettings + MOV AL,[EAX].TFormatSettings.DecimalSeparator + MOV DecimalSep,AL + MOV EAX,FormatSettings + MOV AL,[EAX].TFormatSettings.ThousandSeparator + MOV ThousandSep,AL + MOV EAX,FormatSettings + MOV EAX,[EAX].TFormatSettings.CurrencyString + MOV CurrencyStr,EAX + MOV EAX,FormatSettings + MOV AL,[EAX].TFormatSettings.CurrencyFormat + MOV CurrFmt,AL + MOV EAX,FormatSettings + MOV AL,[EAX].TFormatSettings.NegCurrFormat + MOV NegCurrFmt,AL + MOV SaveGOT,0 + MOV EAX,19 + CMP CL,fvExtended + JNE @@2 + MOV EAX,Precision + CMP EAX,2 + JGE @@1 + MOV EAX,2 +@@1: CMP EAX,18 + JLE @@2 + MOV EAX,18 +@@2: MOV Precision,EAX + PUSH EAX + MOV EAX,9999 + CMP Format,ffFixed + JB @@3 + MOV EAX,Digits +@@3: PUSH EAX + LEA EAX,FloatRec + CALL FloatToDecimal + MOV EDI,Buffer + MOVZX EAX,FloatRec.Exponent + SUB EAX,7FFFH + CMP EAX,2 + JAE @@4 + MOV ECX, EAX + CALL @@PutSign + LEA ESI,@@INFNAN[ECX+ECX*2] + ADD ESI,SaveGOT + MOV ECX,3 + REP MOVSB + JMP @@7 +@@4: LEA ESI,FloatRec.Digits + MOVZX EBX,Format + CMP BL,ffExponent + JE @@6 + CMP BL,ffCurrency + JA @@5 + MOVSX EAX,FloatRec.Exponent + CMP EAX,Precision + JLE @@6 +@@5: MOV BL,ffGeneral +@@6: LEA EBX,@@FormatVector[EBX*4] + ADD EBX,SaveGOT + MOV EBX,[EBX] + ADD EBX,SaveGOT + CALL EBX +@@7: MOV EAX,EDI + SUB EAX,Buffer + POP EBX + POP ESI + POP EDI + JMP @@Exit + +@@FormatVector: + DD @@PutFGeneral + DD @@PutFExponent + DD @@PutFFixed + DD @@PutFNumber + DD @@PutFCurrency + +@@INFNAN: DB 'INFNAN' + +// Get digit or '0' if at end of digit string + +@@GetDigit: + + LODSB + OR AL,AL + JNE @@a1 + MOV AL,'0' + DEC ESI +@@a1: RET + +// Store '-' if number is negative + +@@PutSign: + + CMP FloatRec.Negative,0 + JE @@b1 + MOV AL,'-' + STOSB +@@b1: RET + +// Convert number using ffGeneral format + +@@PutFGeneral: + + CALL @@PutSign + MOVSX ECX,FloatRec.Exponent + XOR EDX,EDX + CMP ECX,Precision + JG @@c1 + CMP ECX,-3 + JL @@c1 + OR ECX,ECX + JG @@c2 + MOV AL,'0' + STOSB + CMP BYTE PTR [ESI],0 + JE @@c6 + MOV AL,DecimalSep + STOSB + NEG ECX + MOV AL,'0' + REP STOSB + JMP @@c3 +@@c1: MOV ECX,1 + INC EDX +@@c2: LODSB + OR AL,AL + JE @@c4 + STOSB + LOOP @@c2 + LODSB + OR AL,AL + JE @@c5 + MOV AH,AL + MOV AL,DecimalSep + STOSW +@@c3: LODSB + OR AL,AL + JE @@c5 + STOSB + JMP @@c3 +@@c4: MOV AL,'0' + REP STOSB +@@c5: OR EDX,EDX + JE @@c6 + XOR EAX,EAX + JMP @@PutFloatExpWithDigits +@@c6: RET + +// Convert number using ffExponent format + +@@PutFExponent: + + CALL @@PutSign + CALL @@GetDigit + MOV AH,DecimalSep + STOSW + MOV ECX,Precision + DEC ECX +@@d1: CALL @@GetDigit + STOSB + LOOP @@d1 + MOV AH,'+' + +@@PutFloatExpWithDigits: + + MOV ECX,Digits + CMP ECX,4 + JBE @@PutFloatExp + XOR ECX,ECX + +// Store exponent +// In AH = Positive sign character ('+' or 0) +// ECX = Minimum number of digits (0..4) + +@@PutFloatExp: + + MOV AL,'E' + MOV BL, FloatRec.Digits.Byte + MOVSX EDX,FloatRec.Exponent + DEC EDX + CALL PutExponent + RET + +// Convert number using ffFixed or ffNumber format + +@@PutFFixed: +@@PutFNumber: + + CALL @@PutSign + +// Store number in fixed point format + +@@PutNumber: + + MOV EDX,Digits + CMP EDX,18 + JB @@f1 + MOV EDX,18 +@@f1: MOVSX ECX,FloatRec.Exponent + OR ECX,ECX + JG @@f2 + MOV AL,'0' + STOSB + JMP @@f4 +@@f2: XOR EBX,EBX + CMP Format,ffFixed + JE @@f3 + MOV EAX,ECX + DEC EAX + MOV BL,3 + DIV BL + MOV BL,AH + INC EBX +@@f3: CALL @@GetDigit + STOSB + DEC ECX + JE @@f4 + DEC EBX + JNE @@f3 + MOV AL,ThousandSep + TEST AL,AL + JZ @@f3 + STOSB + MOV BL,3 + JMP @@f3 +@@f4: OR EDX,EDX + JE @@f7 + MOV AL,DecimalSep + TEST AL,AL + JZ @@f4b + STOSB +@@f4b: JECXZ @@f6 + MOV AL,'0' +@@f5: STOSB + DEC EDX + JE @@f7 + INC ECX + JNE @@f5 +@@f6: CALL @@GetDigit + STOSB + DEC EDX + JNE @@f6 +@@f7: RET + +// Convert number using ffCurrency format + +@@PutFCurrency: + + XOR EBX,EBX + MOV BL,CurrFmt.Byte + MOV ECX,0003H + CMP FloatRec.Negative,0 + JE @@g1 + MOV BL,NegCurrFmt.Byte + MOV ECX,040FH +@@g1: CMP BL,CL + JBE @@g2 + MOV BL,CL +@@g2: ADD BL,CH + LEA EBX,@@MoneyFormats[EBX+EBX*4] + ADD EBX,SaveGOT + MOV ECX,5 +@@g10: MOV AL,[EBX] + CMP AL,'@' + JE @@g14 + PUSH ECX + PUSH EBX + CMP AL,'$' + JE @@g11 + CMP AL,'*' + JE @@g12 + STOSB + JMP @@g13 +@@g11: CALL @@PutCurSym + JMP @@g13 +@@g12: CALL @@PutNumber +@@g13: POP EBX + POP ECX + INC EBX + LOOP @@g10 +@@g14: RET + +// Store currency symbol string + +@@PutCurSym: + + PUSH ESI + MOV ESI,CurrencyStr + TEST ESI,ESI + JE @@h1 + MOV ECX,[ESI-4] + REP MOVSB +@@h1: POP ESI + RET + +// Currency formatting templates + +@@MoneyFormats: + DB '$*@@@' + DB '*$@@@' + DB '$ *@@' + DB '* $@@' + DB '($*)@' + DB '-$*@@' + DB '$-*@@' + DB '$*-@@' + DB '(*$)@' + DB '-*$@@' + DB '*-$@@' + DB '*$-@@' + DB '-* $@' + DB '-$ *@' + DB '* $-@' + DB '$ *-@' + DB '$ -*@' + DB '*- $@' + DB '($ *)' + DB '(* $)' + +@@Exit: +end; + + +resourcestring + SInvalidFloat = '''%s'' is not a valid floating point value'; + +function StrToFloat(const S: string; + const FormatSettings: TFormatSettings): Extended; +begin + if not TextToFloat(PChar(S), Result, fvExtended, FormatSettings) then + raise EConvertError.CreateResFmt(@SInvalidFloat, [S]); +end; + +function FloatToStr(Value: Extended; + const FormatSettings: TFormatSettings): string; +var + Buffer: array[0..63] of Char; +begin + SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended, + ffGeneral, 15, 0, FormatSettings)); +end; + +function FormatDateTime(const Format: string; DateTime: TDateTime; + const FormatSettings: TFormatSettings): string; +begin + DateTimeToString(Result, Format, DateTime, FormatSettings); +end; + +procedure DateTimeToString(var Result: string; const Format: string; + DateTime: TDateTime; const FormatSettings: TFormatSettings); +var + BufPos, AppendLevel: Integer; + Buffer: array[0..255] of Char; + + procedure AppendChars(P: PChar; Count: Integer); + var + N: Integer; + begin + N := SizeOf(Buffer) - BufPos; + if N > Count then N := Count; + if N <> 0 then Move(P[0], Buffer[BufPos], N); + Inc(BufPos, N); + end; + + procedure AppendString(const S: string); + begin + AppendChars(Pointer(S), Length(S)); + end; + + procedure AppendNumber(Number, Digits: Integer); + const + Format: array[0..3] of Char = '%.*d'; + var + NumBuf: array[0..15] of Char; + begin + AppendChars(NumBuf, FormatBuf(NumBuf, SizeOf(NumBuf), Format, + SizeOf(Format), [Digits, Number])); + end; + + procedure AppendFormat(Format: PChar); + var + Starter, Token, LastToken: Char; + DateDecoded, TimeDecoded, Use12HourClock, + BetweenQuotes: Boolean; + P: PChar; + Count: Integer; + Year, Month, Day, Hour, Min, Sec, MSec, H: Word; + + procedure GetCount; + var + P: PChar; + begin + P := Format; + while Format^ = Starter do Inc(Format); + Count := Format - P + 1; + end; + + procedure GetDate; + begin + if not DateDecoded then + begin + DecodeDate(DateTime, Year, Month, Day); + DateDecoded := True; + end; + end; + + procedure GetTime; + begin + if not TimeDecoded then + begin + DecodeTime(DateTime, Hour, Min, Sec, MSec); + TimeDecoded := True; + end; + end; + + function ConvertEraString(const Count: Integer) : string; + var + FormatStr: string; + SystemTime: TSystemTime; + Buffer: array[Byte] of Char; + P: PChar; + begin + Result := ''; + with SystemTime do + begin + wYear := Year; + wMonth := Month; + wDay := Day; + end; + + FormatStr := 'gg'; + if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime, + PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then + begin + Result := Buffer; + if Count = 1 then + begin + case SysLocale.PriLangID of + LANG_JAPANESE: + Result := Copy(Result, 1, CharToBytelen(Result, 1)); + LANG_CHINESE: + if (SysLocale.SubLangID = SUBLANG_CHINESE_TRADITIONAL) + and (ByteToCharLen(Result, Length(Result)) = 4) then + begin + P := Buffer + CharToByteIndex(Result, 3) - 1; + SetString(Result, P, CharToByteLen(P, 2)); + end; + end; + end; + end; + end; + + function ConvertYearString(const Count: Integer): string; + var + FormatStr: string; + SystemTime: TSystemTime; + Buffer: array[Byte] of Char; + begin + Result := ''; + with SystemTime do + begin + wYear := Year; + wMonth := Month; + wDay := Day; + end; + + if Count <= 2 then + FormatStr := 'yy' // avoid Win95 bug. + else + FormatStr := 'yyyy'; + + if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime, + PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then + begin + Result := Buffer; + if (Count = 1) and (Result[{$IFNDEF NEXTGEN}1{$ELSE}0{$ENDIF}] = '0') then + Result := Copy(Result, 2, Length(Result)-1); + end; + end; + + function StrCharLength(const Str: PChar): Integer; + begin + if SysLocale.FarEast then + Result := Integer(CharNext(Str)) - Integer(Str) + else + Result := 1; + end; + + function StrNextChar(const Str: PChar): PChar; + begin + Result := CharNext(Str); + end; + + begin + if (Format <> nil) and (AppendLevel < 2) then + begin + Inc(AppendLevel); + LastToken := ' '; + DateDecoded := False; + TimeDecoded := False; + Use12HourClock := False; + while Format^ <> #0 do + begin + Starter := Format^; + if Starter in LeadBytes then + begin + AppendChars(Format, StrCharLength(Format)); + Format := StrNextChar(Format); + LastToken := ' '; + Continue; + end; + Format := StrNextChar(Format); + Token := Starter; + if Token in ['a'..'z'] then Dec(Token, 32); + if Token in ['A'..'Z'] then + begin + if (Token = 'M') and (LastToken = 'H') then Token := 'N'; + LastToken := Token; + end; + case Token of + 'Y': + begin + GetCount; + GetDate; + if Count <= 2 then + AppendNumber(Year mod 100, 2) else + AppendNumber(Year, 4); + end; + 'G': + begin + GetCount; + GetDate; + AppendString(ConvertEraString(Count)); + end; + 'E': + begin + GetCount; + GetDate; + AppendString(ConvertYearString(Count)); + end; + 'M': + begin + GetCount; + GetDate; + case Count of + 1, 2: AppendNumber(Month, Count); + 3: AppendString(FormatSettings.ShortMonthNames[Month]); + else + AppendString(FormatSettings.LongMonthNames[Month]); + end; + end; + 'D': + begin + GetCount; + case Count of + 1, 2: + begin + GetDate; + AppendNumber(Day, Count); + end; + 3: AppendString(FormatSettings.ShortDayNames[DayOfWeek(DateTime)]); + 4: AppendString(FormatSettings.LongDayNames[DayOfWeek(DateTime)]); + 5: AppendFormat(Pointer(FormatSettings.ShortDateFormat)); + else + AppendFormat(Pointer(FormatSettings.LongDateFormat)); + end; + end; + 'H': + begin + GetCount; + GetTime; + BetweenQuotes := False; + P := Format; + while P^ <> #0 do + begin + if P^ in LeadBytes then + begin + P := StrNextChar(P); + Continue; + end; + case P^ of + 'A', 'a': + if not BetweenQuotes then + begin + if ( (StrLIComp(P, 'AM/PM', 5) = 0) + or (StrLIComp(P, 'A/P', 3) = 0) + or (StrLIComp(P, 'AMPM', 4) = 0) ) then + Use12HourClock := True; + Break; + end; + 'H', 'h': + Break; + '''', '"': BetweenQuotes := not BetweenQuotes; + end; + Inc(P); + end; + H := Hour; + if Use12HourClock then + if H = 0 then H := 12 else if H > 12 then Dec(H, 12); + if Count > 2 then Count := 2; + AppendNumber(H, Count); + end; + 'N': + begin + GetCount; + GetTime; + if Count > 2 then Count := 2; + AppendNumber(Min, Count); + end; + 'S': + begin + GetCount; + GetTime; + if Count > 2 then Count := 2; + AppendNumber(Sec, Count); + end; + 'T': + begin + GetCount; + if Count = 1 then + AppendFormat(Pointer(FormatSettings.ShortTimeFormat)) else + AppendFormat(Pointer(FormatSettings.LongTimeFormat)); + end; + 'Z': + begin + GetCount; + GetTime; + if Count > 3 then Count := 3; + AppendNumber(MSec, Count); + end; + 'A': + begin + GetTime; + P := Format - 1; + if StrLIComp(P, 'AM/PM', 5) = 0 then + begin + if Hour >= 12 then Inc(P, 3); + AppendChars(P, 2); + Inc(Format, 4); + Use12HourClock := TRUE; + end else + if StrLIComp(P, 'A/P', 3) = 0 then + begin + if Hour >= 12 then Inc(P, 2); + AppendChars(P, 1); + Inc(Format, 2); + Use12HourClock := TRUE; + end else + if StrLIComp(P, 'AMPM', 4) = 0 then + begin + if Hour < 12 then + AppendString(FormatSettings.TimeAMString) else + AppendString(FormatSettings.TimePMString); + Inc(Format, 3); + Use12HourClock := TRUE; + end else + if StrLIComp(P, 'AAAA', 4) = 0 then + begin + GetDate; + AppendString(FormatSettings.LongDayNames[DayOfWeek(DateTime)]); + Inc(Format, 3); + end else + if StrLIComp(P, 'AAA', 3) = 0 then + begin + GetDate; + AppendString(FormatSettings.ShortDayNames[DayOfWeek(DateTime)]); + Inc(Format, 2); + end else + AppendChars(@Starter, 1); + end; + 'C': + begin + GetCount; + AppendFormat(Pointer(FormatSettings.ShortDateFormat)); + GetTime; + if (Hour <> 0) or (Min <> 0) or (Sec <> 0) then + begin + AppendChars(' ', 1); + AppendFormat(Pointer(FormatSettings.LongTimeFormat)); + end; + end; + '/': + if DateSeparator <> #0 then + AppendChars(@FormatSettings.DateSeparator, 1); + ':': + if TimeSeparator <> #0 then + AppendChars(@FormatSettings.TimeSeparator, 1); + '''', '"': + begin + P := Format; + while (Format^ <> #0) and (Format^ <> Starter) do + begin + if Format^ in LeadBytes then + Format := StrNextChar(Format) + else + Inc(Format); + end; + AppendChars(P, Format - P); + if Format^ <> #0 then Inc(Format); + end; + else + AppendChars(@Starter, 1); + end; + end; + Dec(AppendLevel); + end; + end; + +begin + BufPos := 0; + AppendLevel := 0; + if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C'); + SetString(Result, Buffer, BufPos); +end; +{$ENDIF} + +{$IFNDEF DELPHI_15} +{$IFDEF DELPHI_12} +function TryStrToBcd(const AValue: string; var Bcd: TBcd; Format: TFormatSettings): Boolean; + + function IsSpaceChar(theChar: Char): Boolean; + begin + Result := False; + if (theChar = ' ') or (theChar = #6) or (theChar = #10) or (theChar = #13) or (theChar = #14) then + Result := True; + end; + +var + Negative: Boolean; + PStr: PChar; + DecimalPos, Exp: Integer; + Pos: Byte; +begin + FillChar(Bcd, SizeOf(Bcd), #0); + PStr := PChar(AValue); + while IsSpaceChar(PStr^) do + Inc(PStr); + Negative := PStr^ = '-'; + if (Pstr^ = '-') or (PStr^ = '+') then + Inc(PStr); + // Skip leading 0s; + while PStr^ = '0' do + Inc(PStr); + + Pos := 0; + DecimalPos := -1; + while PStr^ <> #0 do + begin + if (PStr^ = Format.DecimalSeparator) then + begin + if DecimalPos <> -1 then Exit(False); + if Pos = 0 then + Inc(Pos); + DecimalPos := Pos; + Inc(PStr); + if (PStr^ = #0) then + Break; + end; + if IsSpaceChar(PStr^) or (PStr^ = 'E') or (PStr^ = 'e') then + Break; + + if (PStr^ < '0') or (PStr^ > '9') then + Exit(False); + + if (Pos = 64) and (DecimalPos = -1) then + Exit(False); // Too many digits + if Pos < 64 then + begin + if (Pos and 1) = 0 then + Bcd.Fraction[Pos div 2] := Byte(Ord(PStr^) - Ord('0')) * $10 + else + Bcd.Fraction[Pos div 2] := (Bcd.Fraction[Pos div 2] and $F0) + Byte(Ord(PStr^) - Ord('0')); + Inc(Pos); + end; + Inc(PStr); + end; + + // Scientific notation + if (PStr^ = 'E') or (PStr^ = 'e') then // Most typical situation: X.XXEYYY. DecimalPos = 1 + begin + if not TryStrToInt(PChar(@PStr[1]), Exp) then + Exit(False); + if DecimalPos < 0 then + begin + DecimalPos := Pos; + Inc(Pos); + end; + if Exp < 0 then + begin + begin + if DecimalPos < -Exp then + begin + bcd.Precision := Pos; + bcd.SignSpecialPlaces := Pos -1; + Exp := Pos - Exp; + Pos := Pos - DecimalPos; + if Exp > MaxFMTBcdFractionSize then + begin + dec(Pos, Exp - MaxFMTBcdFractionSize); + DecimalPos := Exp - MaxFMTBcdFractionSize; + Exp := MaxFMTBcdFractionSize; + end; + if not NormalizeBcd(bcd, bcd, Exp, Pos) then + Exit(False); + Pos := Exp; + end + else + Inc(DecimalPos, Exp); + + end; + end + else + begin + inc(DecimalPos, Exp); + if DecimalPos > Pos then + begin + Pos := DecimalPos; + DecimalPos := -1; + end; + end; + + end else + begin + while IsSpaceChar(PStr^) do + Inc(PStr); + if PStr^ <> #0 then + Exit(False); + end; + + if Pos = 0 then + begin + Bcd.Precision := 10; + Bcd.SignSpecialPlaces := 2; + end + else + begin + if Pos > 64 then + Exit(False); + Bcd.Precision := Pos; + if DecimalPos = -1 then + Bcd.SignSpecialPlaces := 0 + else + Bcd.SignSpecialPlaces := Pos - DecimalPos; + // Because it's easier to shift bytes than nibbles, + // Always make it an even precision, add a 0 if needed + if (Pos and 1) = 1 then + begin + Inc(Bcd.Precision); + Inc(Bcd.SignSpecialPlaces); + end; + + if Negative then + Bcd.SignSpecialPlaces := Bcd.SignSpecialPlaces or $80; + end; + Result := True; +end; + +procedure BcdErrorFmt(const Message, BcdAsString: string); +begin + raise EBcdException.CreateFmt(Message, [BcdAsString]); +end; + +procedure OverflowError(const Message: string); +begin + raise EBcdOverflowException.Create(Message); +end; + +function StrToBcd(const AValue: string; Format: TFormatSettings): TBcd; +begin + if not TryStrToBcd(AValue, Result, Format) then + BcdErrorFmt(SInvalidBcdValue, AValue); +end; + +procedure PutChar(var Buf: PChar; C: Char); +begin + Buf^ := C; + Inc(Buf); +end; + +function BcdToStr(const Bcd: TBcd; Format: TFormatSettings): string; +var + Buf: array [0..66] of Char; //64 Nibbles + 1 sign + 1 decimal + #0 + PBuf: PChar; + DecimalPos: Byte; + I: Integer; +begin + if Bcd.Precision = 0 then + Exit('0'); + if (Bcd.Precision > MaxFMTBcdFractionSize) or + ((Bcd.SignSpecialPlaces and $3F) > Bcd.Precision) then + OverflowError(SBcdOverflow); + PBuf := @Buf[1]; + DecimalPos := Bcd.Precision - Bcd.SignSpecialPlaces and $3F; + for I := 0 to Bcd.Precision - 1 do + begin + if I = DecimalPos then + begin + if I = 0 then + PutChar(PBuf, '0'); + PutChar(PBuf, Format.DecimalSeparator); + end; + if (I and 1) = 0 then + PutChar(PBuf, Char( ((Bcd.Fraction[I div 2] and $F0) SHR 4) + ord('0')) ) + else + PutChar(PBuf, Char( ((Bcd.Fraction[I div 2] and $0F)) + ord('0')) ); + end; + // Remove trailing 0s after decmial + Dec(PBuf); + I := Bcd.Precision; + while (I > DecimalPos) and (PBuf^ = '0') do + begin + Dec(PBuf); + Dec(I); + end; + if PBuf^ = Format.DecimalSeparator then + PBuf^ := #0 + else + PBuf[1] := #0; + PBuf := @Buf[1]; + // Remove leading 0s before decimal + while PBuf^ = '0' do + Inc(PBuf); + if (PBuf^ = #0) or (PBuf^ = Format.DecimalSeparator) then + Dec(PBuf); + + if ((Bcd.SignSpecialPlaces and $80) = $80) and + not ((PBuf^ = '0') and (PBuf[1] = #0)) then // only add - if not 0 + begin + Dec(PBuf); + PBuf^ := '-'; + end; + Result := PChar(PBuf); +end; +{$ENDIF} +{$ENDIF} + +function _PQSendQuery(AConnection: TNativeConnect; AQuery: string): integer; +var Q: PAnsiDACChar; + S: DACAString; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin +{$IFDEF M_DEBUG} + LogDebugMessage('SEND', String(AQuery)); +{$ENDIF} + if AConnection.IsUnicodeUsed then + S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AQuery)) + else + S := DACAString(AQuery); + GetMem(Q, Length(S) + 1); + try + {$IFNDEF NEXTGEN} + DACStrCopy(Q, PAnsiChar(S)); + {$ELSE} + DACStrCopy(Q, M.AsAnsi(S).ToPointer); + {$ENDIF} + Result := PQsendQuery(AConnection.Handle, Q); + finally + FreeMem(Q); + end; +end; + +function _PQExecute(AConnection: TNativeConnect; AQuery: string): PPGResult; +var Q: PAnsiDACChar; + S: DACAString; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if AConnection.IsUnicodeUsed then + S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AQuery)) + else + S := DACAString(AQuery); + GetMem(Q, Length(S) + 1); + try + {$IFNDEF NEXTGEN} + DACStrCopy(Q, PAnsiChar(S)); + {$ELSE} + DACStrCopy(Q, M.AsAnsi(S).ToPointer); + {$ENDIF} + Result := PQExec(AConnection.Handle, Q); + finally + FreeMem(Q); + end; +end; + +function _PQexecPrepared(AConnection: TNativeConnect; AStmName: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; +var Q: PAnsiDACChar; + S: DACAString; + paramValues: array of PAnsiDACChar; + i: integer; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if AConnection.IsUnicodeUsed then + S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AStmName)) + else + S := DACAString(AStmName); + GetMem(Q, Length(S) + 1); + try + {$IFNDEF NEXTGEN} + DACStrCopy(Q, PAnsiChar(S)); + {$ELSE} + DACStrCopy(Q, M.AsAnsi(S).ToPointer); + {$ENDIF} + SetLength(paramValues, AParams.Count); + for i := 0 to AParams.Count - 1 do + begin + if AConnection.IsUnicodeUsed then + S := {$IFDEF NEXTGEN}string{$ENDIF}(UTF8Encode(AParams[i].AsString)) + else + S := DACAString(AParams[i].AsString); + GetMem(paramValues[i], Length(S) + 1); + {$IFNDEF NEXTGEN} + DACStrCopy(paramValues[i], PAnsiChar(S)); + {$ELSE} + DACStrCopy(paramValues[i], M.AsAnsi(S).ToPointer); + {$ENDIF} + end; + try + Result := PQexecPrepared(AConnection.Handle, Q, AParams.Count, @paramValues[0], nil, nil, AResultFormat); + finally + for i := 0 to AParams.Count - 1 do + FreeMem(paramValues[i]); + end; + finally + FreeMem(Q); + end; +end; + +function _PQExecuteParams(AConnection: TNativeConnect; AQuery: string; AParams: TParams; AResultFormat: integer = 0): PPGResult; +var Q: PAnsiDACChar; + S: DACAString; + paramValues: array of PAnsiDACChar; + i: integer; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if AConnection.IsUnicodeUsed then + S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AQuery)) + else + S := DACAString(AQuery); + GetMem(Q, Length(S) + 1); + try + {$IFNDEF NEXTGEN} + DACStrCopy(Q, PAnsiChar(S)); + {$ELSE} + DACStrCopy(Q, M.AsAnsi(S).ToPointer); + {$ENDIF} + SetLength(paramValues, AParams.Count); + for i := 0 to AParams.Count - 1 do + begin + if AParams[i].IsNull then + begin + paramValues[i] := nil; + Continue; + end; + if AConnection.IsUnicodeUsed then + S := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AParams[i].AsString)) + else + S := DACAString(AParams[i].AsString); + GetMem(paramValues[i], Length(S) + 1); + {$IFNDEF NEXTGEN} + DACStrCopy(paramValues[i], PAnsiChar(S)); + {$ELSE} + DACStrCopy(paramValues[i], M.AsAnsi(S).ToPointer); + {$ENDIF} + end; + try + Result := PQexecParams(AConnection.Handle, Q, AParams.Count, nil, @paramValues[0], nil, nil, AResultFormat); + finally + for i := 0 to AParams.Count - 1 do + FreeMem(paramValues[i]); + end; + finally + FreeMem(Q); + end; +end; + +function _PQConnectDBParams(AParams: TStrings; ExpandDbName: boolean = False): PPGConn; +var + ConnKeywords, ConnValues: packed array of PAnsiDACChar; + K,V: DACAString; + i: integer; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + SetLength(ConnKeywords, AParams.Count + 1); + SetLength(ConnValues, AParams.Count + 1); + for i := 0 to AParams.Count - 1 do + begin + K := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AParams.Names[i])); //since this is connection assume we'll use UTF8 + GetMem(ConnKeywords[i], Length(K) + 1); + {$IFNDEF NEXTGEN} + DACStrCopy(ConnKeywords[i], PAnsiChar(K)); + {$ELSE} + DACStrCopy(ConnKeywords[i], M.AsAnsi(K).ToPointer); + {$ENDIF} + {$IFDEF DELPHI_7} + V := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(AParams.ValueFromIndex[i])); + {$ELSE} + V := UTF8Encode(Copy(AParams[I], Length(K) + 2, MaxInt)); + {$ENDIF} + GetMem(ConnValues[i], Length(V) + 1); + {$IFNDEF NEXTGEN} + DACStrCopy(ConnValues[i], PAnsiChar(V)); + {$ELSE} + DACStrCopy(ConnValues[i], M.AsAnsi(V).ToPointer); + {$ENDIF} + end; + try + {$IFDEF M_DEBUG} + LogDebugMessage('CONN', AParams.CommaText); + {$ENDIF} + ConnKeywords[High(ConnKeywords)] := nil; + ConnValues[High(ConnValues)] := nil; + + Result := PQconnectdbParams(@ConnKeywords[0], @ConnValues[0], ord(ExpandDbName)); + finally + for i := 0 to AParams.Count - 1 do + begin + FreeMem(ConnValues[i]); + FreeMem(ConnKeywords[i]); + end; + end; +end; + +function TimeOf(const ADateTime: TDateTime): TDateTime; +var + Hour, Min, Sec, MSec: Word; +begin + DecodeTime(ADateTime, Hour, Min, Sec, MSec); + Result := EncodeTime(Hour, Min, Sec, MSec); +end; + +function AdjustNativeField(iField :TPSQLField; Src,Dest: Pointer; Var Blank : Boolean): Word; +begin + Result := 0; + Blank := PAnsiDACChar(Src)^ = #0; + if Blank then Exit; + Inc(PAnsiDACChar(Src)); + case iField.NativeType of + FIELD_TYPE_BOOL: SmallInt(Dest^) := SmallInt(Src^); + + FIELD_TYPE_INT2: SmallInt(Dest^) := SmallInt(Src^); + FIELD_TYPE_INT4: LongInt(Dest^) := LongInt(Src^); + FIELD_TYPE_INT8: Int64(Dest^) := Int64(Src^); + + FIELD_TYPE_DATE: LongInt(Dest^) := DateTimeToTimeStamp(TDateTime(Src^)).Date; + FIELD_TYPE_TIME: LongInt(Dest^) := DateTimeToTimeStamp(TDateTime(Src^)).Time; + FIELD_TYPE_TIMESTAMP: TDateTime(Dest^):= TimeStampToMSecs(DateTimeToTimeStamp(TDateTime(Src^))); + + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: Double(Dest^) := Double(Src^); + + FIELD_TYPE_NUMERIC: TBcd(Dest^) := TBcd(Src^); + +{$IFDEF DELPHI_12} + FIELD_TYPE_POINT: TPSQLPoint(Dest^) := TPSQLPoint(Src^); + FIELD_TYPE_CIRCLE: TPSQLCircle(Dest^) := TPSQLCircle(Src^); + FIELD_TYPE_BOX: TPSQLBox(Dest^) := TPSQLBox(Src^); + FIELD_TYPE_LSEG: TPSQLLSeg(Dest^) := TPSQLLSeg(Src^); + + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE : TPSQLRange(Dest^) := TPSQLRange(Src^); +{$ENDIF DELPHI_12} + + FIELD_TYPE_BYTEA, + FIELD_TYPE_OID, + FIELD_TYPE_TEXT: Result := 1; + else + StrLCopy(PChar(Dest), PChar(Src), iField.FieldLength - 1); //minus null byte + end; + Blank := Result <> 0; +end; + +function AdjustDelphiField(iField: TPSQLField; Src, Dest: Pointer): Word; +var + TimeStamp: TTimeStamp; +begin + {$IFNDEF NEXTGEN} + ZeroMemory(Dest, iField.NativeSize); + {$ELSE} + FillChar(Dest^, iField.NativeSize, 0); + {$ENDIF} + PAnsiDACChar(Dest)^ := #1; + Inc(PAnsiDACChar(Dest), 1); + Result:=0; + + case iField.NativeType of + FIELD_TYPE_BOOL: SmallInt(Dest^) := SmallInt(Src^); + FIELD_TYPE_INT2: SmallInt(Dest^) := SmallInt(Src^); + FIELD_TYPE_INT4: LongInt(Dest^) := LongInt(Src^); + FIELD_TYPE_INT8: Int64(Dest^) := Int64(Src^); + FIELD_TYPE_DATE: begin + try + TimeStamp.Date := LongInt(Src^); + TimeStamp.Time := 0; + TDateTime(Dest^) := TimeStampToDateTime(TimeStamp); + except + Result := 1; + end; + end; + FIELD_TYPE_TIME: begin + try + TimeStamp.Date := DateDelta; + TimeStamp.Time := LongInt(Src^); + TDateTime(Dest^) := TimeStampToDateTime(TimeStamp); + except + Result := 1; + end; + end; + + FIELD_TYPE_TIMESTAMP: begin + try + TDateTime(Dest^):= TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ELSE}Double{$ENDIF}(Src^))); + except + Result:=1; + end; + end; + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: Double(Dest^) := Double(Src^); + FIELD_TYPE_NUMERIC: TBcd(Dest^) := TBcd(Src^); + +{$IFDEF DELPHI_12} + FIELD_TYPE_POINT: TPSQLPoint(Dest^) := TPSQLPoint(Src^); + FIELD_TYPE_CIRCLE: TPSQLCircle(Dest^) := TPSQLCircle(Src^); + FIELD_TYPE_BOX: TPSQLBox(Dest^) := TPSQLBox(Src^); + FIELD_TYPE_LSEG: TPSQLLSeg(Dest^) := TPSQLLSeg(Src^); + + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: TPSQLRange(Dest^) := TPSQLRange(Src^); +{$ENDIF DELPHI_12} + + FIELD_TYPE_OID, + FIELD_TYPE_BYTEA, + FIELD_TYPE_TEXT: Result := 1; + else + {$IFDEF DELPHI_12} + if iField.NativeDataset.FConnect.IsUnicodeUsed then + {$IFNDEF NEXTGEN} + CopyMemory(Dest, Src, iField.NativeSize) + {$ELSE} + Move(Src^, Dest^, iField.NativeSize) + {$ENDIF} + else + {$ENDIF} + DACStrCopy(PAnsiDACChar(Dest), PAnsiDACChar(Src), iField.NativeSize); + end; + + if Result = 1 then + begin + {$IFNDEF NEXTGEN} + ZeroMemory(Dest, iField.NativeSize); + {$ELSE} + FillChar(Dest^, iField.NativeSize, 0); + {$ENDIF} + Result := 0; + end; +end; + +procedure PSQLException(PSQL : TNativeConnect); +begin + raise EPSQLException.Create(PSQL); +end; + +procedure PSQLExceptionMsg(PSQL : TNativeConnect; Const ErrorMsg : String ); +begin + raise EPSQLException.CreateMsg(PSQL, ErrorMsg ); +end; + +function BDETOPSQLStr(Field : TFieldDef): String; +const + _IsVarChar: array[boolean] of string = ('CHAR','VARCHAR'); + _IntNames: array[boolean,boolean] of string = (('INT4','SERIAL'),('INT8','BIGSERIAL')); +var + isAutoInc: Boolean; + isInt8: boolean; + + ColName: string; +begin + Result :=''; + ColName := AnsiQuotedStr(Field.Name,'"'); + case Field.DataType of + ftString, + ftFixedChar : begin + Result := Format('%s %s',[ColName,_IsVarChar[(Field.DataType = ftFixedChar) or (faFixed in Field.Attributes)]]); + if Field.Size > 0 then + Result := Result + Format('(%s)',[IntToStr(Field.Size)]); + end; + + ftDate : Result := Format('%s DATE',[ColName]); + ftBlob, + ftBytes, + ftVarBytes : Result := Format('%s BYTEA',[ColName]); + ftMemo : Result := Format('%s TEXT',[ColName]); + ftBoolean : Result := Format('%s BOOL',[ColName]); + ftSmallint, + ftWord + {$IFDEF DELPHI_15} + ,ftShortInt + {$ENDIF} + : Result := Format('%s INT2',[ColName]); + + ftInteger, + {$IFDEF DELPHI_15} + ftLongWord, + {$ENDIF} + ftLargeint, + ftAutoInc : begin + isAutoInc := ftAutoInc = Field.DataType; + isInt8 := Field.DataType = ftLargeint; + Result := Format('%s %s',[ColName,_IntNames[isInt8,isAutoInc]]); + end; + + ftFloat, + ftBCD : Result := Format('%s NUMERIC(%s,%s)',[ColName,IntToStr(Field.Size),IntToStr(Field.Precision)]); + + ftTime : Result := Format('%s TIME',[ColName]); + + {$IFDEF DELPHI_15} + ftTimeStamp, + {$ENDIF} + ftDateTime: Result := Format('%s DATETIME',[ColName]); + end; + if Field.Required then + Result := Result + ' NOT NULL'; +end; + +function SQLCreateIdxStr(Index : TPSQLIndex;TableName : String;Flds : TPSQLFields): String; + + function GetFieldList:String; + var + I : Integer; + S : String; + begin + S :=''; + for I :=0 to Index.FldsInKey-1 do + begin + S := S+ AnsiQuotedStr(Flds.Field[Index.Description.aiKeyFld[I]].FieldName,'"'); + if I < Index.FldsInKey-1 then S := S+','; + end; + Result := S; + end; + +var IdxName: string; + Tbl: PChar; + +begin + result := ''; + + if Index.IndexName = '' then + begin + Tbl := Pchar(TableName); + IdxName := AnsiExtractQuotedStr(Tbl,'"'); + if Index.Primary then + idxName := 'PK_'+IdxName + else + if Index.Unique then + idxName := 'UNI_'+IdxName + else + idxName := 'IDX_'+IdxName + end + else + IdxName := Index.IndexName; + IdxName := AnsiQuotedStr(IdxName,'"'); + + if Index.Primary then + Result := Format('ALTER TABLE %s ADD CONSTRAINT %s PRIMARY KEY(%s);',[TableName,IdxName,GetFieldList]) + else + if Index.Unique then + Result := Format('CREATE UNIQUE INDEX %s ON %s (%s);',[IdxName,TableName,GetFieldList]) + else + Result := Format('CREATE INDEX %s ON %s (%s);',[IdxName,TableName,GetFieldList]); +end; + +{******************************************************************************} +{ EPSQLError *} +{******************************************************************************} +constructor EPSQLException.CreateBDE(ECode : Word); +begin + Inherited Create(''); + FBDEErrorCode := ECode; + FBDE := True; +end; + +constructor EPSQLException.CreateBDEMsg(ECode : Word; Const EMessage : string); +begin + inherited; + FPSQLErrorMsg := string(EMessage); + CreateBDE(ECode); +end; + +constructor EPSQLException.Create(PSQL : TNativeConnect); +begin + FPSQL := PSQL; + FPSQLErrorCode := 1; + FPSQLErrorMsg := PSQL.GetErrorText; + if FPSQLErrorCode > 0 then FBDEERRORCode := DBIERR_INVALIDPARAM; + Inherited Create(''); +end; + +constructor EPSQLException.CreateMsg(PSQL : TNativeConnect; const ErrorMsg : String ); +begin + Create(PSQL); + FPSQLErrorMsg := ErrorMsg; + FBDEERRORCode := 1001; +end; + +{******************************************************************************} +{ TNativeConnect *} +{******************************************************************************} +constructor TNativeConnect.Create; +begin + Inherited Create; + FLoggin := False; + FHandle := nil; + FGUCList := TStringList.Create; +end; + +Destructor TNativeConnect.Destroy; +begin + InternalDisconnect; + FreeAndNil(FGUCList); + Inherited Destroy; +end; + +procedure TNativeConnect.DirectExecute(SQL: string); +var + LocHandle: PPGconn; + locResult: PPGresult; + ErrStr: String; + OldLoggin : Boolean; +begin + if SQLLibraryHandle <= HINSTANCE_ERROR then LoadPSQLLibrary(); + OldLoggin := FLoggin; + if FLoggin then InternalDisconnect; + with DBOptions do + LocHandle := PQconnectdb(PAnsiDACChar(UTF8Encode((FDirectConnectString)))); + if not Assigned(LocHandle) then Exit; + LocResult := _PQExecute(Self, SQL); + if Assigned(LocResult) then + begin + ErrStr := RawToString(PQerrorMessage(LocHandle)); + PQclear(LocResult); + end; + PQfinish(LocHandle); + if OldLoggin then InternalConnect; + if ErrStr <> '' then + raise EPSQLException.CreateMsg(self,ErrStr); +end; + +{$IFDEF DELPHI_5} +function ifThen(aCondition: boolean; IfTrue, IfFalse: string): string; +begin + if aCondition then Result := IfTrue else Result := IfFalse; +end; + +function ifThen(aCondition: boolean; IfTrue: integer; IfFalse: integer = 0): integer; +begin + if aCondition then Result := IfTrue else Result := IfFalse; +end; +{$ENDIF} + +class function TNativeConnect.Ping(Params: TStrings): TPingStatus; +var ConnStr: string; + i: integer; +begin + ConnStr := ''; + for i := 0 to Params.Count - 1 do + ConnStr := ConnStr + Params[i] + ' '; + Result := PQping(PAnsiDACChar(Utf8Encode(ConnStr))); +end; + +procedure TNativeConnect.ProcessDBParams(Params : TStrings); +var i: integer; +begin + for i := 0 to Params.Count - 1 do + begin + FConnectString := FConnectString + Params[i] + ' '; + if Params.Names[i] = 'dbname' then + FDirectConnectString := FDirectConnectString + 'dbname=template1 ' + else + FDirectConnectString := Params[i] + ' '; + end; +end; + +procedure TNativeConnect.InternalConnect(ConnParams: TStrings = nil); +var + Result: PPGresult; + Utf8Encoded: PAnsiDACChar; +begin + if not FLoggin then + try + FHandle := nil; + if SQLLibraryHandle <= HINSTANCE_ERROR then + LoadPSQLLibrary(); + FLastOperationTime := GetTickCount; + if Assigned(ConnParams) then + FHandle := _PQConnectDBParams(ConnParams) + else + begin + Utf8Encoded := PAnsiDACChar(UTF8Encode(FConnectString)); + FHandle := PQConnectDB(Utf8Encoded); + end; + FLastOperationTime := GetTickCount - FLastOperationTime; + if PQstatus(FHandle) = CONNECTION_BAD then + CheckResult(); + Result := PQExec(FHandle, 'SET DateStyle TO ''ISO, MDY'''); + PQclear(Result); + FLoggin := True; + ReloadGUC(); + MonitorHook.DBConnect(Self, True); + except + MonitorHook.DBConnect(Self, False); + if Assigned(FHandle) then + PQfinish(FHandle); + raise; + end; +end; + +function TNativeConnect.GetBackendPID: Integer; +begin + Result := PQbackendPID(Handle); +end; + +procedure TNativeConnect.InternalDisconnect; +begin + if FLoggin then + begin + FLastOperationTime := GetTickCount; + PQfinish(Handle); + FLastOperationTime := GetTickCount - FLastOperationTime; + Handle := nil; + FLoggin := False; + FServerVersion := ''; + FGUCList.Clear; + MonitorHook.DBDisconnect(Self); + end; +end; + +function TNativeConnect.GetErrorText: String; +begin + Result := RawToString(PQerrorMessage(Handle)); +end; + +function TNativeConnect.GetNativeByteaFormat: TNativeByteaFormat; +begin + if FGUCList.Values['bytea_output'] = 'hex' then + Result := nbfHex + else + Result := nbfEscape; +end; + +function TNativeConnect.Success: Boolean; +begin + Result := GetErrorText = ''; +end; + +function TNativeConnect.RollBack: boolean; +var + Res: PPGresult; +begin + Res := PQexec(Handle, 'ROLLBACK'); + Result := PQresultStatus(Res) = PGRES_COMMAND_OK; + PQclear(Res); +end; + +function TNativeConnect.Commit: boolean; +var + Res: PPGresult; +begin + Res := PQexec(Handle, 'COMMIT'); + Result := PQresultStatus(Res) = PGRES_COMMAND_OK; + PQclear(Res); +end; + + +procedure TNativeConnect.CheckResult; +var S: string; +begin + S := GetErrorText(); + if S > '' then + begin + FErrorSeverity := ''; + FErrorSQLState := ''; + FErrorPrimary := ''; + FErrorDetail := ''; + FErrorHint := ''; + FErrorInternalPos := ''; + FErrorInternalQuery := ''; + FErrorSourceFile := ''; + FErrorSourceLine := ''; + FErrorSourceFunc := ''; + FErrorContext := ''; + FErrorPos := ''; + FErrorSchemaName := ''; + FErrorTableName := ''; + FErrorColumnName := ''; + FErrorDataTypeName := ''; + FErrorConstraintName := ''; + raise EPSQLException.CreateMsg(Self, S); + end; +end; + +procedure TNativeConnect.CheckResult(FStatement: PPGresult); +var + S: string; +begin + S := GetErrorText(); + if S > '' then + begin + FErrorSeverity := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SEVERITY))); + FErrorSQLState := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SQLSTATE))); + FErrorPrimary := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_MESSAGE_PRIMARY))); + FErrorDetail := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_MESSAGE_DETAIL))); + FErrorHint := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_MESSAGE_HINT))); + FErrorInternalPos := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_INTERNAL_POSITION))); + FErrorInternalQuery := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_INTERNAL_QUERY))); + FErrorSourceFile := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SOURCE_FILE))); + FErrorSourceLine := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SOURCE_LINE))); + FErrorSourceFunc := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SOURCE_FUNCTION))); + FErrorContext := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_CONTEXT))); + FErrorPos := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_STATEMENT_POSITION))); + FErrorSchemaName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_SCHEMA_NAME))); + FErrorTableName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_TABLE_NAME))); + FErrorColumnName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_COLUMN_NAME))); + FErrorDataTypeName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_DATATYPE_NAME))); + FErrorConstraintName := Trim(RawToString(PQresultErrorField(FStatement, PG_DIAG_CONSTRAINT_NAME))); + raise EPSQLException.CreateMsg(Self, S); + end; +end; + +procedure TNativeConnect.TableList(pszWild: string; SystemTables: Boolean; List: TStrings); +var + I : LongInt; + sql : String; + RES : PPGresult; +begin + InternalConnect; + List.Clear; + + Sql := 'SELECT c.oid :: regclass FROM pg_class as c, pg_namespace as ns' + + ' WHERE c.relkind IN (''r'', ''v'') AND (ns.oid = c.relnamespace)'; + + if not SystemTables then + Sql := SQL + ' AND (ns.nspname NOT LIKE ''pg_%'')'+ + ' AND (ns.nspname NOT LIKE ''information_schema'')'; + + if pszWild > '' then + Sql := Sql + ' AND relname LIKE '''+ pszWild+ ''''; + Sql := Sql + ' ORDER BY 1'; + RES := _PQExecute(Self, Sql); + try + CheckResult; + if Assigned(RES) then + for I := 0 to PQntuples(RES) - 1 do + List.Add(RawToString(PQgetvalue(RES,I,0))); + finally + PQclear(RES); + end; +end; + +procedure TNativeConnect.UserList(pszWild : string; List : TStrings); +var + I : LongInt; + sql : String; + RES : PPGresult; +begin + InternalConnect; + Sql := 'SELECT usename FROM pg_shadow '; + if pszWild <> '' then + Sql := Sql + ' WHERE usename LIKE ''' + pszWild + ''''; + Sql := Sql + ' ORDER BY 1'; + RES := _PQExecute(Self, Sql); + try + if Assigned(RES) then + begin + CheckResult; + for I := 0 to PQntuples(RES)-1 do + begin + List.Add(RawToString(PQgetvalue(RES,I,0))); + end; + end; + finally + PQclear(RES); + end; +end; + +procedure TNativeConnect.SchemaList(pszWild : string; SystemSchemas: Boolean; List : TStrings); +var + I : LongInt; + sql : String; + RES : PPGresult; +begin + InternalConnect; + Sql := 'SELECT nspname FROM pg_namespace WHERE True '; + if pszWild <> '' then + Sql := Sql + ' AND nspname LIKE ''' + pszWild + ''''; + if not SystemSchemas then + begin + Sql := Sql + ' AND nspname NOT IN (''pg_catalog'', ''pg_toast'', '+ + '''pg_sysviews'', ''information_schema'') '; + if GetserverVersionAsInt > 080200 then + Sql := Sql + 'AND nspname NOT LIKE E''pg\\_temp\\_%'' AND nspname NOT LIKE E''pg\\_toast_temp\\_%''' + else + Sql := Sql + 'AND nspname NOT LIKE ''pg\\_temp\\_%'' AND nspname NOT LIKE ''pg\\_toast_temp\\_%''' + end; + Sql := Sql + ' ORDER BY 1'; + RES := _PQExecute(Self, Sql); + try + if Assigned(RES) then + begin + CheckResult; + for I := 0 to PQntuples(RES)-1 do + begin + List.Add(RawToString(PQgetvalue(RES,I,0))); + end; + end; + finally + PQclear(RES); + end; +end; + +procedure TNativeConnect.DatabaseList(pszWild : string; List :TStrings); +var + CRec : string; + I : LongInt; + sql : String; + RES : PPGresult; +begin + InternalConnect; + List.Clear; + Sql := 'SELECT datname FROM pg_database'; + if pszWild <> '' then + Sql := Sql + ' WHERE datname LIKE '''+pszWild+''''; + Sql := Sql + ' ORDER BY datname'; + RES := _PQExecute(Self, Sql); + if Assigned(RES) then + begin + for I := 0 to PQntuples(RES)-1 do + begin + CREC := RawToString(PQgetvalue(RES,I,0)); + List.Add(CREC); + end; + end; + PQclear(RES); +end; + +procedure TNativeConnect.OpenTable(pszTableName: string; + pszIndexName: string; + iIndexId: Word; + eOpenMode: DBIOpenMode; + eShareMode: DBIShareMode; + var hCursor: hDBICur; + AnOptions: TPSQLDatasetOptions; + Limit, Offset : integer); +begin + InternalConnect; + if FSystem then + begin + hCursor := hDBICur(TNativeDataSet.Create(Self, AnOptions, pszTableName, pszIndexName, iIndexId,1,0,True)); + FSystem := False; + end else + hCursor := hDBICur(TNativeDataSet.Create(Self, AnOptions, pszTableName, pszIndexName, iIndexId,Limit,Offset)); + TNativeDataSet(hCursor).OpenTable; +end; + +procedure TNativeConnect.QueryAlloc(var hStmt: hDBIStmt); +begin + hStmt := hDBIStmt(TNativeDataSet.Create(Self, [], '' , '', 0, 0, 0)); +end; + +procedure TNativeConnect.QueryPrepare(var hStmt: hDBIStmt;Query : String); +begin + FLastOperationTime := GetTickCount; + TNativeDataSet(hStmt).SQLQuery := Query; + TNativeDataSet(hStmt).isQuery := True; + FLastOperationTime := GetTickCount - FLastOperationTime; + MonitorHook.SQLPrepare(TNativeDataSet(hStmt)); +end; + +procedure TNativeConnect.BeginTran(eXIL: eXILType; var hXact: hDBIXact); +var + Res: PPGresult; + Status: ExecStatusType; + TransParam: DACAString; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if FTransState = xsActive then Exit; + hXact := hDBIXact(Self); + TransParam := 'START TRANSACTION ISOLATION LEVEL '; + case eXIL of + xilDIRTYREAD, + xilREADCOMMITTED: TransParam := TransParam + 'READ COMMITTED'; + xilREPEATABLEREAD: TransParam := TransParam + 'REPEATABLE READ'; + xilSERIALIZABLE: TransParam := TransParam + 'SERIALIZABLE'; + end; + Res := PQExec(Handle, {$IFNDEF NEXTGEN}PAnsiChar(TransParam){$ELSE}M.AsAnsi(TransParam).ToPointer{$ENDIF}); + try + Status := PQresultStatus(Res); + MonitorHook.TRStart(Self, Status = PGRES_COMMAND_OK); + if Status = PGRES_COMMAND_OK then + begin + FTransState := xsActive; + FTransLevel := eXIL; + end + else + CheckResult; + finally + PQclear(Res); + end +end; + +procedure TNativeConnect.EndTran(hXact : hDBIXact; eEnd : eXEnd); +begin + if eEnd = xendABORT then + MonitorHook.TRRollback(Self, Rollback) + else + MonitorHook.TRCommit(Self, Commit); + CheckResult; + FTransState := xsInactive; +end; + +procedure TNativeConnect.GetTranInfo(hXact : hDBIXact; pxInfo : pXInfo); +begin + {$IFNDEF NEXTGEN} + ZeroMemory(pxInfo, Sizeof(pxInfo^)); + {$ELSE} + FillChar(pxInfo^, Sizeof(pxInfo^), 0); + {$ENDIF} + if GetTransactionStatus in [trstACTIVE, trstINTRANS, trstINERROR] then + FTransState := xsActive; + pxInfo^.eXState := FTransState; + pxInfo^.eXIL := FTransLevel; +end; + +procedure TNativeConnect.QExecDirect(pszQuery : String; phCur: phDBICur; var AffectedRows : integer); +var + hStmt : hDBIStmt; +begin + hStmt := nil; + QueryAlloc(hStmt); + QueryPrepare(hStmt, pszQuery); + if hStmt <> nil then + try + FLastOperationTime := GetTickCount; + TNativeDataSet(hStmt).Execute; + finally + FLastOperationTime := GetTickCount - FLastOperationTime; + AffectedRows := TNativeDataSet(hStmt).FAffectedRows; + TNativeDataSet(hStmt).Free; + end; +end; + +procedure TNativeConnect.OpenFieldList(pszTableName: string; + pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur); +var + P: TNativeDataSet; + Props: curProps; + Items: Integer; + Descs: TFLDDescList; +begin + FSystem := True; + OpenTable(pszTableName, '', 0, dbiREADONLY, dbiOPENSHARED, hDBICur(P), + [], 0, 0); + P.GetCursorProps(Props); + Items := Props.iFields; + if Items > 0 then + begin + SetLength(Descs, Items); + try + P.GetFieldDescs(Descs); + hCur := hDBICur(TFieldList.Create(Self, Descs, Items)); + finally + Finalize(Descs); + end; + end; + P.CloseTable; + P.Free; + FSystem := False; +end; + +procedure TNativeConnect.OpenIndexList(pszTableName: string; pszDriverType: string; var hCur: hDBICur); +var + P : hDBICur; + Ind : TIndexList; + + procedure ProcessTable; + var + Props : CURProps; + Items : Word; + Descs : TIDXDescList; + begin + Descs := nil; + TNativeDataSet(P).GetCursorProps(Props); + Items := Props.iIndexes; + try + if Items > 0 then + begin + SetLength(Descs, Items); + TNativeDataSet(P).GetIndexDescs(Descs); + end else + Descs := nil; + Ind := TIndexList.Create(Self, Descs, Items); + hCur := hDBICur(Ind); + finally + Finalize(Descs); + end; + end; + + procedure OpenAndProcessTable; + begin + FSystem := True; + OpenTable(pszTableName, '', 0, dbiREADONLY, dbiOPENSHARED, P, [], 0, 0); + try + ProcessTable; + TNativeDataSet(P).CloseTable; + finally + TNativeDataSet(P).Free; + end; + FSystem := False; + end; + +begin + hCur := nil; + try + OpenAndProcessTable; + except + On E:EPSQLException do OpenAndProcessTable; + end; +end; + +function TNativeConnect.GetCharSet: string; +begin + Result := 'UNDEFINED'; + if not FLoggin then Exit; + Result := UpperCase(string(PQparameterStatus(Handle, 'client_encoding'))); + FCharset := Result; + FUnicodeUsed := (FCharset = 'UNICODE') or (FCharset = 'UTF8'); +end; + +procedure TNativeConnect.EmptyTable(hCursor : hDBICur; pszTableName : string); +var + isNotOpen : Boolean; +begin + isNotOpen := not Assigned(hCursor); + if isNotOpen then + OpenTable(pszTableName, '', 0, dbiREADWRITE, dbiOPENEXCL, hCursor, [], 0, 0); + try + TNativeDataSet(hCursor).EmptyTable; + finally + if isNotOpen then + TNativeDataSet(hCursor).Free; + end; +end; + +procedure TNativeConnect.AddIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; var IdxDesc: IDXDesc; pszKeyviolName: string); +var + NDS : TNativeDataSet; +begin + if Assigned(hCursor) then + NDS := TNativeDataSet(hCursor) else + OpenTable(pszTableName, '', IdxDesc.iIndexId, dbiREADWRITE, dbiOPENEXCL, hDBICur(NDS), [], 0, 0); + try + NDS.AddIndex(idxDesc,pszKeyViolName); + finally + if not Assigned(hCursor) then NDS.Free; + end; +end; + +procedure TNativeConnect.DeleteIndex(hCursor: hDBICur; pszTableName: string; pszDriverType: string; pszIndexName: string; pszIndexTagName: string; iIndexId: Word); +var + NDS : TNativeDataSet; +begin + if Assigned(hCursor) then + NDS := TNativeDataSet(hCursor) else + OpenTable(pszTableName, pszIndexName, iIndexId, dbiREADWRITE, dbiOPENEXCL, hDBICur(NDS), [], 0, 0); + try + NDS.DeleteIndex(pszIndexName, pszIndexTagName, iIndexID); + finally + if not Assigned(hCursor) then NDS.Free; + end; +end; + + +////////////////////////////////////////////////////////// +//constructor : TPSQLField.CreateField +//Description : constructor CreateNewField +////////////////////////////////////////////////////////// +//Input : Owner: TCollection +// P: pFldDesc +// FNum: Word +// LType: Word +// LSize: Word +////////////////////////////////////////////////////////// +constructor TPSQLField.CreateField(Owner : TCollection; P : FldDesc; P1 : VCHKDesc; FNum, LType, LSize : integer; isArray : Boolean); +begin + Create(Owner); + + FDesc := P; + FValCheck := P1; + + FieldNumber := FNum; + NativeType := LType; + Case NativeType of + FIELD_TYPE_BYTEA: NativeBLOBType := nbtBytea; + FIELD_TYPE_OID: NativeBLOBType := nbtOID + else + NativeBLOBType := nbtNotBlob; + end; + NativeSize := LSize; //Max(LSize, FDesc.iLen); + + FieldArray := isArray; +end; + +function TPSQLField.GetFieldName : String; +begin + Result := string(FDesc.szName); +end; + +procedure TPSQLField.SetFieldName(Const Value : String); +begin + StrPCopy(@FDesc.szName, Copy(Value,1,SizeOf(FDesc.szName)-1)); +end; + +procedure TPSQLField.SetBuffer(PRecord : Pointer); +begin + FBuffer := PRecord; + if FBuffer <> nil then + begin + FData := FBuffer; + Inc(PAnsiDACChar(FData), FDesc.iOffset); + if FDesc.INullOffset > 0 then + begin + FStatus := FBuffer; + Inc(PAnsiDACChar(FStatus), FDesc.iNullOffset); + end else + FStatus := NIL; + end else + begin + FData := nil; + FStatus := nil; + end; +end; + +function TPSQLField.GetNativeDataset: TNativeDataSet; +begin + Result := TPSQLFields(Collection).NativeDataset; +end; + +function TPSQLField.GetNativeConnect: TNativeConnect; +begin + Result := TPSQLFields(Collection).NativeConnect; +end; + +function TPSQLField.GetNull : Boolean; +var AVal: PAnsiDACChar; +begin + Result := True; + case NativeType of + FIELD_TYPE_BPCHAR, + FIELD_TYPE_VARCHAR: if (dsoEmptyCharAsNull in GetNativeDataset.Options) then + begin + AVal := FieldValue; + Inc(AVal); + if AVal = '' then Exit; //we have empty string and it's treated as NULL due options + end; + end; + if FStatus <> nil then + Result := TFieldStatus(FStatus^).isNULL = -1 + else + Result := FALSE; +end; + +procedure TPSQLField.SetNull( Flag : Boolean ); +Const + VALUES : Array[ Boolean ] of SmallInt = ( 0, -1 ); +begin + if FStatus <> nil then FStatus^.isNULL := VALUES[ Flag ]; +end; + +function TPSQLField.GetChanged : Boolean; +begin + if FStatus <> nil then Result := TFieldStatus(FStatus^).Changed else Result := TRUE; +end; + +procedure TPSQLField.SetChanged(Flag : Boolean); +begin + if FStatus <> nil then TFieldStatus(FStatus^).Changed := Flag; +end; + +function TPSQLField.GetLocalSize : integer; +begin + Result := FDesc.iUnused[1]; +end; + +procedure TPSQLField.SetLocalSize(S : integer); +begin + FDesc.iUnused[1] := S; +end; + +function TPSQLField.GetLocalType : integer; +begin + Result := FDesc.iUnused[0]; +end; + +procedure TPSQLField.SetLocalType(S : integer); +begin + FDesc.iUnused[0] := S; +end; + +function TPSQLField.FieldValue: PAnsiDACChar; +begin + Result := PAnsiDACChar(PAnsiDACChar(FData) + FieldNumber - 1); +end; + +function TPSQLField.FieldValueAsStr: string; + var Src: pointer; + + function SimpleQuote(const S: string): string; + begin + Result := '''' + S + ''''; + end; + +begin + Src := FieldValue(); + Inc(PAnsiDACChar(Src)); + case FieldType of + fldBOOL: Result := IfThen(SmallInt(Src^) > 0, 'TRUE', 'FALSE'); + fldINT16: Result := IntToStr(SmallInt(Src^)); + fldINT32: Result := IntToStr(LongInt(Src^)); + fldINT64: Result := IntToStr(Int64(Src^)); + fldFloat: Result := SQLFloatToStr(Double(Src^)); +{$IFDEF DELPHI_12} + fldFMTBCD: Result := BcdToStr(TBcd(Src^), PSQL_FS); +{$ENDIF} + fldZSTRING: + case NativeType of + FIELD_TYPE_BIT, + FIELD_TYPE_VARBIT: Result := 'B' + NativeDataset.StrValue(Src) + else + Result := NativeDataset.StrValue(Src); + end; + fldUUID: Result := NativeDataset.UuidValue(Src); + fldBLOB: if FieldSubType = fldstMemo then + Result := NativeDataset.MemoValue(Src) + else + Result := NativeDataset.BlobValue(Src, Self); + fldDate: Result := SimpleQuote(DateTimeToSqlDate(TDateTime(Src^), DATE_MODE)); + fldTime: Result := SimpleQuote(DateTimeToSqlDate(TDateTime(Src^), TIME_MODE)); + fldTIMESTAMP: Result := SimpleQuote(DateTimeToSqlDate(TDateTime(Src^), TIMESTAMP_MODE)); +{$IFDEF DELPHI_12} + fldPOINT: Result := SimpleQuote(PointToSQLPoint(TPSQLPoint(Src^))); + fldCIRCLE: Result := SimpleQuote(CircleToSQLCircle(TPSQLCircle(Src^))); + fldBOX: Result := SimpleQuote(BoxToSQLBox(TPSQLBox(Src^))); + fldLSEG: Result := SimpleQuote(LSegToSQLLSeg(TPSQLLSeg(Src^))); + fldRANGE: Result := SimpleQuote(RangeToSQLRange(TPSQLRange(Src^), NativeType)); +{$ENDIF DELPHI_12} + end; +end; + +function TPSQLField.GetFieldDefault : string;//mi +begin + Result := string(FValCheck.aDefVal); +end; + +procedure TPSQLField.SetFieldDefault(aStr : string);//mi +begin + FValCheck.aDefVal := aStr; +end; + +////////////////////////////////////////////////////////// +// TPSQLFields +////////////////////////////////////////////////////////// + +function TPSQLFields.AddField(P: FldDesc; P1: VCHKDesc; FNum, LType, + LSize: integer; isArray: Boolean): TPSQLField; +begin + Result := Add as TPSQLField; + Result.Description := P; + Result.ValCheck := P1; + Result.FieldNumber := FNum; + Result.NativeType := LType; + case Result.NativeType of + FIELD_TYPE_BYTEA: Result.NativeBLOBType := nbtBytea; + FIELD_TYPE_OID: Result.NativeBLOBType := nbtOID + else + Result.NativeBLOBType := nbtNotBlob; + end; + Result.NativeSize := LSize; //Max(LSize, Result.FieldLength); + Result.FieldArray := isArray; +end; + +constructor TPSQLFields.Create(Table : TNativeDataSet); +begin + Inherited Create(TPSQLField); + FTable := Table; +end; + + +function TPSQLFields.GetField(Index : Integer) : TPSQLField; +var + LocType : Integer; + LocSize : Integer; + LocArray : Boolean; + Desc : FldDesc; + ValCheck : VCHKDesc; +begin + if ( Count >= Index ) and ( Index > 0 ) then + Result := TPSQLField(Items[Index-1]) else + begin + if not ((Index > 0) and (FTable <> nil)) then raise EPSQLException.CreateBDE(DBIERR_INVALIDRECSTRUCT); + FTable.GetNativeDesc(Index, Desc, ValCheck, LocType, LocSize, LocArray); + Result := TPSQLField.CreateField(Self, Desc, ValCheck, Index, LocType, LocSize,LocArray); + end; +end; + +function TPSQLFields.GetNativeConnect: TNativeConnect; +begin + Result := FTable.Connect; +end; + + +procedure TPSQLFields.SetFields(PRecord : Pointer); +var + i : integer; +begin + For i := 1 to Count do + begin + With Field[i] do + begin + Buffer := PRecord; + FieldChanged := FALSE; + FieldNull := TRUE; + end; + end; +end; + +function TPSQLFields.FieldNumberFromName(SearchName : PChar) : Integer; +var + I : Integer; +begin + Result := 0; + For i := 1 to Count do + begin + With GetField( i ) do + begin + if (StrIComp(SearchName, PChar(FieldName)) = 0) then + begin + Result := Integer(FieldNumber); + Exit; + end; + end; + end; +end; + +////////////////////////////////////////////////////////// +//constructor : TPSQLIndex.CreateIndex +//Description : constructor CreateIndex +////////////////////////////////////////////////////////// +//Input : Owner: TCollection +// P: pIDXDesc +////////////////////////////////////////////////////////// +constructor TPSQLIndex.CreateIndex(Owner : TCollection; P : pIDXDesc); +begin + Create(Owner); + FDesc := P^; +end; + +constructor TPSQLIndexes.Create(Table : TNativeDataSet); +begin + Inherited Create(TPSQLIndex); + FTable := Table; +end; + +function TPSQLIndexes.GetIndex(Index : Integer) : TPSQLIndex; +begin + Result := nil; + if ( Count >= Index ) and ( Index > 0 ) then Result := TPSQLIndex(Items[Index-1]); +end; + +function TPSQLIndexes.FindByName(Name :String): TPSQLIndex; +var + I : Integer; +begin + Result := nil; + for i := 0 to Count-1 do + begin + if (CompareText(TPSQLIndex(Items[I]).IndexName, Name) = 0) then + begin + Result := TPSQLIndex(Items[I]); + Exit; + end; + end; +end; + +function TPSQLIndexes.SetIndex(Name,Fields : String;aPrimary,aUnique,aDesc : Boolean): integer; +var + Item : TPSQLIndex; + I,K,J : Integer; + FldLen : integer; + FieldList : TStrings; + + //this is used, because Postgres didn't delete dropped columns, + //but only mark them as deleted. See pg_attribute for details + function GetLogicalIndexByPhysical(const PhNum: integer):integer; + var I: integer; + begin + Result := 0; + For I:=0 to FTable.FieldCount-1 do + if FTable.FieldPosInTable(I) = PhNum then + begin + Result := I+1; + Break; + end; + end; + +begin + Result := -1; + Item := FindByName(Name); + if not Assigned(Item) then + begin + Item := TPSQLIndex(Add); + Item.IndexNumber := Item.Index+1; + Item.IndexName := Name; + end; + + Item.Primary := aPrimary; + Item.Unique := aUnique; + Item.Descending := aDesc; + Item.FDesc.bMaintained := True; + FieldList := TStringList.Create; + try + FieldList.CommaText := Fields; + for J := 0 to FieldList.Count-1 do + begin + I := StrToIntDef(FieldList[J],0); + if FTable.FConnect.GetserverVersionAsInt >= 070400 then + I := GetLogicalIndexByPhysical(I); + + if I = 0 then //we have index built on expressions. + begin + Item.Free; + Exit; + end; + FldLen := FTable.FFieldDescs.GetField(I).NativeSize; //utf indices built on varchar need 2 bytes per character + Item.FldsInKey := Item.FldsInKey+1; + Item.BlockSize := Item.BlockSize+FldLen; + Item.KeyLen := Item.BlockSize+Item.FldsInKey; + K :=Item.FldsInKey; + Item.FDesc.aiKeyFld[K-1] := I; + Result := Item.IndexNumber; + end; + finally + FieldList.Free; + end; +end; + +function TPSQLIndexes.FieldNumberFromName( SearchName : PChar ) : Integer; +var + I : Integer; +begin + Result := 0; + if FTable.FFieldDescs.Count = 0 then FTable.InitFieldDescs; + For i := 1 to FTable.FFieldDescs.Count do + begin + With FTable.FFieldDescs.GetField(i) do + begin + if (StrIComp(SearchName, PChar(FieldName))= 0) then + begin + Result := Integer(FieldNumber); + Exit; + end; + end; + end; +end; + +////////////////////////////////////////////////////////// +//constructor : TPSQLNative.CreateNative +//Description : constructor CreateNative +////////////////////////////////////////////////////////// +//Input : Owner: TCollection +////////////////////////////////////////////////////////// +constructor TPSQLNative.CreateNative(Owner : TCollection; P : PPGFIELD_INFO); +begin + Create(Owner); + FDesc := P^; +end; + + +constructor TPSQLNatives.Create(Table : TNativeDataSet); +begin + Inherited Create(TPSQLNative); + FTable := Table; +end; + +function TPSQLNatives.GetNative(Index : Integer) : TPSQLNative; +begin + Result := nil; + if ( Count >= Index ) and ( Index > 0 ) then Result := TPSQLNative(Items[Index-1]); +end; + +procedure TPSQLNatives.SetNative(aIndex : Integer; aName : String; aType, aSize, aMaxSize, aTypMod : Integer); +var + Item : TPSQLNative; +begin + Item := TPSQLNative(Add); + Item.NativeNumber := aIndex; + Item.NativeName := aName; + Item.NativeType := aType; + Item.NativeSize := aSize; + Item.NativeMaxSize := aMaxSize; + Item.NativeTypMod := aTypMod; +end; + + +////////////////////////////////////////////////////////// +//Description : TPSQLFilter impementation +////////////////////////////////////////////////////////// +constructor TPSQLFilter.Create(Owner : TNativeDataSet; AClientData : Longint; Exp : pCANExpr; pfFilt : pfGENFilter); +begin + Inherited Create; + FDataset := Owner; + FClientData := AClientData; + if Assigned(Exp) then + begin + FExprSize := CANExpr(Exp^).iTotalSize; + if FExprSize > 0 then + begin + GetMem(FExpression, FExprSize); + if Assigned(FExpression) then Move(Exp^, FExpression^, FExprSize); + end; + end; + FPfFilter:= pfFilt; + FActive:= FALSE; +end; + +Destructor TPSQLFilter.Destroy; +begin + if (FExprSize > 0) and Assigned(FExpression) then FreeMem(FExpression, FExprSize); + Inherited Destroy; +end; + +function TPSQLFilter.GetFilterResult(PRecord : Pointer) : Variant; +var + I : Integer; +begin + if FActive then + begin + FRecBuff := PRecord; + if Assigned(FpfFilter) then + begin + i := 0; + try + i := FpfFilter(FClientData, FRecBuff, Longint(0)); + finally + result := i <> 0; + end; + end else + begin + if Assigned(FExpression) then + begin + try + Result := CalcExpression(GetNodeByOffset(NodeStart)); + if Result = Null then Result := False; + except + Result := FALSE; + end; + end; + end; + end else Result := False; +end; + +function TPSQLFilter.GetNodeStart : Integer; +begin + Result := FExpression.iNodeStart; +end; + +function TPSQLFilter.GetLiteralPtr(AOffset: Word): Pointer; +var + i : word; +begin + i := CANExpr(FExpression^).iLiteralStart + AOffset; + Result := @MemPtr(FExpression)^[i]; +end; + +function TPSQLFilter.GetNodeByOffset(AOffSet : Integer) : PCanNode; +begin + Result := pCanNode(DACPointerInt(FExpression)+AOffset); +end; + +function TPSQLFilter.CalcExpression(ANode : PCanNode) : Variant; +Var + FldType : TFldType; +begin + Case pCanHdr(ANode).nodeClass Of + PSQLTypes.nodeUNARY : Result := UnaryNode(pCANUnary(ANode)); + PSQLTypes.nodeBINARY : Result := BinaryNode(pCANBinary(ANode)); + PSQLTypes.nodeCOMPARE : Result := CompareNode(pCANCompare(ANode)); + PSQLTypes.nodeFIELD : Result := FieldNode(pCANField(ANode)); + PSQLTypes.nodeCONST : Result := PerformCanConst(PCANConst(ANode), + GetLiteralPtr(PCANConst(ANode).iOffset), FldType); + PSQLTypes.nodeLISTELEM : Result := ListOfValues(pCANListElem(ANode)); + else + result := Null; + End; +end; + +function TPSQLFilter.ListOfValues(ANode : pCANListElem) : Variant; +Var + I : Integer; + CurNode : pCANListElem; +begin + CurNode := ANode; + I := 0; + While True Do + begin + Inc(I); + if CurNode^.iNextOffset = 0 Then break; + CurNode := pCanListElem(GetNodeByOffset(NodeStart + CurNode^.iNextOffset)); + end; + Result := varArrayCreate([1, I], varVariant); + I := 1; + While True Do + begin + Result[ I ] := CalcExpression(PCanNode(GetNodeByOffset(NodeStart + ANode^.iOffset))); + if ANode^.iNextOffset = 0 Then break; + ANode := pCanListElem(GetNodeByOffset(NodeStart + ANode^.iNextOffset)); + Inc(I); + end; +end; + +function TPSQLFilter.PerformLikeCompare(Const Value, Mask : String; CaseSen : Boolean) : Boolean; +begin + Result := PSQLExtMask.MatchesMask(Value, Mask, CaseSen); +end; + +function TPSQLFilter.PerformInCompare(AOp1, AOp2 : Variant) : Boolean; +Var + Save : Variant; + I, Top : Integer; +begin + if varType(AOp1) = varArray then + begin + Save := AOp2; + AOp2 := AOp1; + AOp1 := Save; + end; + Result := True; + Top := VarArrayHighBound(AOp2, 1); + For I := VarArrayLowBound(AOp2, 1) to Top do + if AOp1 = AOp2[I] then Exit; + Result := False; +end; + +function TPSQLFilter.UnaryNode( ANode : PCANUnary ) : Variant; +begin + With ANode^ Do Result := PerformCANOp(canOp, GetNodeValue(iOperand1), UnAssigned); +end; + +function TPSQLFilter.BinaryNode(ANode : PCANBinary) : Variant; +begin + With ANode^ Do Result := PerformCANOp(canOp, GetNodeValue(iOperand1), GetNodeValue(iOperand2)); +end; + +function TPSQLFilter.CompareNode(ANode : PCANCompare) : Variant; +Var + Op1, Op2 : Variant; +begin + Op1 := GetNodeValue(Anode^.iOperand1); + Op2 := GetNodeValue(Anode^.iOperand2); + if varIsNull(Op1) Or varIsEmpty(Op1) Then Op1 := ''; + if varIsNull(Op2) Or varIsEmpty(Op2) Then Op2 := ''; + if ANode.canOp = canLike then + Result := PerformLikeCompare(Op1, Op2, not ANode^.bCaseInsensitive) else + begin + Result := Search(Op1, Op2, OEMConv, not Anode^.bCaseInsensitive, Anode^.iPartialLen); + if Anode^.canOp = canNE then Result := not Result; + end; + +end; + +function TPSQLFilter.FieldNode(ANode : pCANField) : Variant; +Var + Field : TPSQLField; + blank : Boolean; + Dest : array[0..MAX_CHAR_LEN] of Char; + TimeStamp : TTimeStamp; + DateD : Double; +begin + Result := Null; + Field := FDataset.Fields[ANode.iFieldNum]; + FDataSet.NativeToDelphi(Field, FrecBuff, @Dest, blank); + if blank then Exit; + case Field.FieldType of + fldINT16: Result := PSmallInt(@Dest)^; + fldUINT16:Result := PWord(@Dest)^; + fldINT32: Result := PLongInt(@Dest)^; + fldUINT32:Result := PLongInt(@Dest)^; + {$IFDEF DELPHI_6} + fldINT64: Result := PInt64(@Dest)^; + {$ENDIF} + fldFLOAT: Result := PDouble(@Dest)^; + fldZSTRING: + {$IFDEF DELPHI_12} + if FDataset.FConnect.IsUnicodeUsed then + Result := string(PChar(@Dest)) + else + {$ENDIF} + Result := string(PAnsiDACChar(@Dest)); + fldUUID: Result := string(PAnsiDACChar(@Dest)); + fldBOOL : Result := PWordBool(@Dest)^; + fldDATE : begin + TimeStamp.Date := PLongWord(@Dest)^; + TimeStamp.Time := 0; + Result := SysUtils.Time+Trunc(TimeStampToDateTime(TimeStamp) + 1E-11); + end; + fldTIME : begin + TimeStamp.Time := PLongWord(@Dest)^; + TimeStamp.Date := 0; + Result := SysUtils.Date+TimeOf(TimeStampToDateTime(TimeStamp)); + end; + fldTIMESTAMP : begin + DateD := PDouble(@Dest)^; + Result := TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF}(DateD))); + end; + else Result := NULL; + end; +end; + +function TPSQLFilter.GetNodeValue(AOffSet : Integer) : Variant; +begin + Result := CalcExpression(GetNodeByOffset(NodeStart + AOffset)); +end; + +function TPSQLFilter.PerformCANOp(AOperator : CANOp; AOp1, AOp2 : Variant) : Variant; +begin + Case AOperator of + canNOTDEFINED : Result := Null; + canISBLANK : Result := VarIsNull(AOp1); + canNOTBLANK : Result := not VarIsNull(AOp1); + canNOT : Result := not AOp1; + canEQ : Result := AOp1 = AOp2; + canNE : Result := AOp1 <> AOp2; + canGT : Result := AOp1 > AOp2; + canLT : Result := AOp1 < AOp2; + canGE : Result := AOp1 >= AOp2; + canLE : Result := AOp1 <= AOp2; + canAND : Result := AOp1 and AOp2; + canOR : Result := AOp1 or AOp2; + canMinus : Result := -AOp1; + canADD : Result := AOp1+AOp2; + canSUB : Result := AOp1-AOp2; + canMUL : Result := AOp1*AOp2; + canDIV : Result := AOp1 / AOp2; + canMOD : Result := AOp1 mod AOp2; + canREM : Result := AOp1 mod AOp2; + canSUM : Result := Null; + canCONT : Result := Null; + canLike : Result := PerformLikeCompare(AOp1,AOp2,True); + canIN : Result := PerformInCompare(AOp1,AOp2); + canUPPER : Result := AnsiUpperCase(AOp1); + canLOWER : Result := AnsiLowerCase(AOp1); + canASSIGN : Result := VarIsNull(AOp1); + Else Result := Null; + end; +end; + +function TPSQLFilter.PerformCanConst(ANode:PCANConst; ValuesStart : Pointer; Var FldType : TFldType) : Variant; + +function _PerformCanConst( ANode : PCANConst; ValuePtr : Pointer; Var FldType : TFldType) : Variant; +Var + Offs : DACPointerInt; + TimeStamp : TTimeStamp; + DateData : Double; + S: String; +{$IFDEF DELPHI_12} + Len: word; + buffer: PChar; +{$ENDIF} +begin + With ANode^ Do + begin + Offs := DACPointerInt(ValuePtr); + FldType := FT_UNK; + Result := Null; + Case iType Of + fldZSTRING : begin + S:= string(PAnsiDACChar(Offs)); + Result := S; + FldType := FT_STRING; + end; +{$IFDEF DELPHI_12} + fldUNICODE : begin + buffer := ValuePtr; + Len := Word(buffer[0]); + Inc(buffer); + SetLength(S, Len div 2); + if Len > 0 then + S := Copy(buffer, 1, Len div 2); + Result := S; + FldType := FT_STRING; + end; +{$ENDIF} + fldDATE : begin + TimeStamp.Date := PLongWord( Offs )^; + TimeStamp.Time := 0; + Result := SysUtils.Time+ Trunc(TimeStampToDateTime(TimeStamp) + 1E-11); + FldType := FT_DATE; + end; + fldBOOL : begin + Result := PWordBool( Offs )^; + FldType := FT_BOOL; + end; + + fldINT16 : begin + Result := PSmallInt( Offs )^; + FldType := FT_INT; + end; + fldINT32 : begin + Result := PInteger( Offs )^; + FldType := FT_INT; + end; + {$IFDEF DELPHI_6} + fldINT64 : begin + Result := Pint64( Offs )^; + FldType := FT_INT; + end; + {$ENDIF} + fldFLOAT : begin + Result := PDouble( Offs )^; + FldType := FT_FLOAT; + end; + fldTIME : begin + TimeStamp.Time := PLongWord( Offs )^; + TimeStamp.Date := 0; + Result := SysUtils.Date+TimeOf(TimeStampToDateTime( TimeStamp )); + FldType := FT_TIME; + end; + + fldTIMESTAMP : begin + DateData := PDouble( Offs )^; + Result := TimeStampToDateTime( MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF} (DateData) ) ); + FldType := FT_DATETIME; + end; + fldUINT16 : begin + Result := PWord( Offs )^; + FldType := FT_INT; + end; + fldUINT32 : begin + Result := PInteger( Offs )^; + FldType := FT_INT; + end; + end; + end; +end; +begin + Result:=_PerformCanConst(ANode,ValuesStart,FldType); +end; + +function TPSQLFilter.TimeOf(const ADateTime: TDateTime): TDateTime; +var + Hour, Min, Sec, MSec: Word; +begin + DecodeTime(ADateTime, Hour, Min, Sec, MSec); + Result := EncodeTime(Hour, Min, Sec, MSec); +end; + +{$O-} +constructor TNativeDataSet.Create(PSQL : TNativeConnect; + //Container : TContainer; + AnOptions: TPSQLDatasetOptions; + AName, IndexName : string; + Index : Word; + Limit, Offset : Integer; + ASystem : Boolean = False); +begin + Inherited Create; + FStatement := nil; + FFilters := TContainer.Create; + if IndexName <> '' then FIndexName := IndexName; + FFieldDescs := TPSQLFields.Create(Self); + FIndexDescs := TPSQLIndexes.Create(Self); + FNativeDescs := TPSQLNatives.Create(Self); + FKeyNumber := 0; + FPrimaryKeyNumber := 0; + AutoReExec := True; + FConnect := PSQL; + FOptions := AnOptions; + FOpen := False; + FRecSize:=-1; + FLimit := Limit; + FOffset := Offset; + StandartClause := TStringList.Create; + OrderClause := TStringList.Create; + RangeClause := TStringList.Create; + LimitClause := TStringList.Create; + TableName := string(AName); + MasterCursor := nil; + FSystemNeed := ASystem; + IsQuery := False; + FPreventRememberBuffer := False; //mi:2008-08-27 +end; + +Destructor TNativeDataSet.Destroy; +begin + MasterCursor := nil; + CloseTable; + ClearIndexInfo; + StandartClause.Free; + OrderClause.Free; + RangeClause.Free; + limitClause.Free; + FNativeDescs.Free; + FIndexDescs.Free; + FFieldDescs.Free; + FFilters.Free; + Inherited Destroy; +end; + +////////////////////////////////////////////////////////// +// PROTECTED METHODS // +////////////////////////////////////////////////////////// +procedure TNativeDataSet.SetBufferAddress(P : Pointer); +begin + FCurrentBuffer := P; +end; + +procedure TNativeDataSet.SetInternalBuffer(Buffer : Pointer); +begin + if not FPreventRememberBuffer then //mi:2008-08-27 check if we need to remember buffer + BufferAddress := Buffer; +end; + +function TNativeDataSet.GetInternalBuffer : Pointer; +begin + Result := FInternalBuffer; +end; + +procedure TNativeDataSet.SetCurrentBuffer(PRecord : Pointer); +begin + FCurrentBuffer := PRecord; +end; + +function TNativeDataSet.GetCurrentBuffer : Pointer; +begin + Result := FCurrentBuffer; +end; + +function TNativeDataSet.FieldOffset(iField: Integer): integer; +var + i: SmallInt; +begin + Result:=0; + if not ((iField>=1) or (iField<=FieldCount)) then raise EPSQLException.CreateBDE(DBIERR_INVALIDPARAM); + Dec(iField); + Dec(iField); + for i:=0 to iField do + begin + case FieldType(I) of + FIELD_TYPE_INT2, + FIELD_TYPE_BOOL: Inc(Result,SizeOf(SmallInt)); + FIELD_TYPE_OID, + FIELD_TYPE_TEXT, + FIELD_TYPE_BYTEA: Inc(Result,SizeOf(TBlobItem)); + FIELD_TYPE_INT4: Inc(Result,SizeOf(LongInt)); + FIELD_TYPE_INT8: Inc(Result,SizeOf(Int64)); + FIELD_TYPE_DATE, + FIELD_TYPE_TIME: Inc(Result,SizeOf(TDateTime)); + FIELD_TYPE_TIMESTAMP: Inc(Result,SizeOf(TDateTime)); + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: Inc(Result, SizeOf(Double)); + FIELD_TYPE_NUMERIC: Inc(Result, SizeOf(TBcd)); + else + Inc(Result,FieldMaxSizeInBytes(I)); + end; + end; +end; + +function TNativeDataSet.GetBookMarkSize : Integer; +begin + Result := Sizeof(TPSQLBookMark); +end; + +procedure TNativeDataSet.SetBufBookmark; +Var + Buffer : Pointer; +begin + if (CurrentBuffer <> nil) and (FBookOfs > 0) then + begin + Buffer := CurrentBuffer; + Buffer := Pointer(Int64(Buffer) + FBookOfs); + GetBookMark(Buffer); + end; +end; + +function TNativeDataSet.GetRecordNumber: Longint; +begin + Result := RecNo; +end; + +procedure TNativeDataSet.SetRecordNumber(RecNom : Longint); +var + Original: LongInt; +begin + Original := RecNom; + if RecNom < 0 then + begin + RecNom := RecordCount; + Dec(RecNom); + try + if RecordState <> tsEmpty then CurrentRecord(RecNom); + except + end; + end else + if RecordState <> tsEmpty then CurrentRecord(RecNom); + Recno := Original; +end; + +function TNativeDataSet.GetRecCount: LongInt; +begin + Result := 0; + if not Assigned(FStatement) then Exit; + Result := PQntuples(FStatement); +end; + +procedure TNativeDataSet.GetRecordCount( var iRecCount : integer ); +var + P : Pointer; + Buff : Pointer; + Marked : Boolean; +begin + if not FFilterActive then + iRecCount := GetRecCount() + else + begin + iRecCount := 0; + GetMem(Buff, GetWorkBufferSize); + try + + GetMem(P, BookMarkSize); + try + try + GetBookMark(P); + Marked := true; + except + on E:EPSQLException do + Marked := false; + end; + + SetToBegin(); + try + repeat + GetNextRecord(dbiNOLOCK, Buff, nil); + Inc(iRecCount); + until false; + except + on E:EPSQLException do; + end; + + if Marked then + SetToBookMark(P) + else + SetToBegin; + finally + FreeMem(P, BookMarkSize); + end; + finally + FreeMem(Buff, GetWorkBufferSize); + end; + end; +end; + +procedure TNativeDataSet.CheckFilter(PRecord : Pointer); +var + P : Pointer; + B : Boolean; + aSize: integer; +begin + if PRecord <> nil then + begin + if FFilterActive then + While not FilteredRecord(PRecord) do + begin + InternalBuffer := PRecord; + if FLastDir <> tdPrev then NextRecord else PrevRecord; + end; + end else + begin + if FFilterActive then + begin + aSize := GetWorkBufferSize; + P := AllocMem(aSize); + try + InternalBuffer := P; + InternalReadBuffer; + B := FilteredRecord(P); + While not B do + begin + InternalBuffer := P; + if FLastDir <> tdPrev then + NextRecord() + else + PrevRecord(); + B := FilteredRecord(P); + end; + finally + FreeMem(P,aSize); + InternalBuffer := nil; + end; + end; + end; +end; + +procedure TNativeDataSet.FirstRecord; +begin + RecNo := 0; + if RecordCount = 0 then + raise EPSQLException.CreateBDE(DBIERR_EOF); + InternalReadBuffer; + SetBufBookmark; + MonitorHook.SQLFetch(Self); +end; + +procedure TNativeDataSet.LastRecord; +begin + if (dsoFetchOnDemand in Options) and not FFetched then + FetchRecords(MaxInt); + RecNo := RecordCount - 1; + if RecNo < 0 then + raise EPSQLException.CreateBDE(DBIERR_EOF); + InternalReadBuffer; + SetBufBookmark; + MonitorHook.SQLFetch(Self); +end; + +procedure TNativeDataSet.NextRecord(); +begin + Inc(RecNo); + if (RecNo >= RecordCount) and (dsoFetchOnDemand in Options) and not FFetched then + FetchRecords(); + if RecNo >= RecordCount then + raise EPSQLException.CreateBDE(DBIERR_EOF); + InternalReadBuffer; + SetBufBookmark; + MonitorHook.SQLFetch(Self); +end; + +procedure TNativeDataSet.PrevRecord(); +begin + if RecNo >= RecordCount then + raise EPSQLException.CreateBDE(DBIERR_BOF); + if RecNo > 0 then + Dec(RecNo) + else + raise EPSQLException.CreateBDE(DBIERR_BOF); + InternalReadBuffer; + SetBufBookmark; + MonitorHook.SQLFetch(Self); +end; + +procedure TNativeDataSet.CurrentRecord(ARecNo : Longint); +begin + RecNo := ARecNo; + if RecNo <= -1 then raise EPSQLException.CreateBDE(DBIERR_BOF); + SetBufBookmark; + InternalReadBuffer; +end; + +procedure TNativeDataSet.GetWorkRecord(eLock: DBILockType;PRecord: Pointer); +var + P : TPSQLBookMark; +begin + GetBookMark(@P); + CheckParam(@P=nil,DBIERR_INVALIDPARAM); + if not FIsLocked then + begin + SetToBookMark(@P); + if eLock = dbiWRITELOCK then LockRecord(eLock); + RecordState := tsPos; + end; +end; + +procedure TNativeDataSet.LockRecord(eLock : DBILockType); +begin + FIsLocked := (eLock <> dbiNOLOCK); +end; + +function TNativeDataSet.FilteredRecord(PRecord : Pointer) : Boolean; +var + P : TPSQLFilter; + I : Integer; +begin + Result := TRUE; + if FFilterActive then + begin + For i := 0 to FFilters.Count-1 do + begin + P := FFilters.Items[i]; + if P.Active and not P.GetFilterResult(PRecord) then + begin + Result := FALSE; + Exit; + end; + end; + end; +end; + +procedure TNativeDataSet.UpdateFilterStatus; +Var + P : TPSQLFilter; + I : Integer; +begin + For i := 0 to FFilters.Count-1 do + begin + P := FFilters.Items[i]; + if (P <> NIL) and (P.Active) then + begin + FFilterActive := TRUE; + Exit; + end; + end; + FFilterActive := FALSE; +end; + +procedure TNativeDataSet.NativeToDelphi(P: TPSQLField; PRecord: Pointer; pDest: Pointer; var bBlank: Boolean); +begin + CheckParam(PRecord = nil, DBIERR_INVALIDPARAM); + P.Buffer := PRecord; + bBlank := P.FieldNull; + if not bBlank and Assigned(pDest) then + AdjustNativeField(P, P.FieldValue, pDest, bBlank); +end; + +procedure TNativeDataSet.DelphiToNative(P: TPSQLField;PRecord: Pointer;pSrc: Pointer); +begin + if pSrc <> nil then AdjustDelphiField(P, pSrc, PAnsiDACChar(P.Data) + P.FieldNumber - 1); +end; + +procedure TNativeDataSet.CheckParam(Exp : Boolean;BDECODE : Word); +begin + if Exp then raise EPSQLException.CreateBDE(BDECODE); +end; + +///////////////////////////////////////////////////////////////////// +// PUBLIC METHODS // +///////////////////////////////////////////////////////////////////// +procedure TNativeDataSet.GetRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); +begin + InternalBuffer := PRecord; + Case RecordState of + tsPos: + begin + GetWorkRecord(eLock,PRecord); + try + CheckFilter(PRecord); + except + On E:EPSQLException do + begin + if FReRead then + begin + FReRead := FALSE; + RecordState := tsNoPos; + GetNextRecord( eLock, PRecord, pRecProps ); + end + else + begin + if eLock = dbiWRITELOCK then FIsLocked := FALSE; + raise; + end; + end; + end; + if pRecProps <> nil then + begin + pRecProps^.iPhyRecNum := RecNo+1; + pRecProps^.iSeqNum := RecNo+1; + end; + end; + tsFirst: raise EPSQLException.CreateBDE(DBIERR_EOF); + tsLast: raise EPSQLException.CreateBDE(DBIERR_BOF); + tsEmpty: + begin + try + GetNextRecord( eLock, PRecord, pRecProps ); + except + On E:EPSQLException do + begin + try + GetPriorRecord( eLock, PRecord, pRecProps ); + except + On E:EPSQLException do + begin + RecordState := tsNoPos; + GetNextRecord( eLock, PRecord, pRecProps ); + end; + end; + end; + end; + end; + else raise EPSQLException.CreateBDE(DBIERR_NOCURRREC); + end; +end; + +procedure TNativeDataSet.GetNextRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); +begin + FLastDir := tdNext; + InternalBuffer := PRecord; + Case RecordState of + tsPos, + tsEmpty: NextRecord; + tsFirst, + tsNoPos: FirstRecord; + else raise EPSQLException.CreateBDE(DBIERR_EOF); + end; + CheckFilter(PRecord); + if eLock <> dbiNOLOCK then GetRecord(eLock, PRecord, pRecProps); + if pRecProps <> nil then + begin + pRecProps^.iPhyRecNum := RecNo+1; + pRecProps^.iSeqNum := RecNo+1; + end; + RecordState := tsPos; +end; + +procedure TNativeDataSet.GetPriorRecord(eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps); +begin + FLastDir := tdPrev; + InternalBuffer := PRecord; + case RecordState of + tsPos, + tsEmpty: PrevRecord; + tsLast, + tsNoPos: LastRecord; + else raise EPSQLException.CreateBDE(DBIERR_BOF); + end; + CheckFilter(PRecord); + if eLock <> dbiNOLOCK then GetRecord(eLock, PRecord, pRecProps); + if pRecProps <> nil then + begin + pRecProps^.iPhyRecNum := RecNo+1; + pRecProps^.iSeqNum := RecNo+1; + end; + RecordState := tsPos; +end; + +procedure TNativeDataSet.AddFilter(iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr;pfFilter: pfGENFilter;var hFilter: hDBIFilter); +var + P : TPSQLFilter; +begin + P := TPSQLFilter.Create(Self,iClientData,pcanExpr,pfFilter); + FFilters.Insert(P); + UpdateFilterStatus; + hFilter := hDBIFilter(P); +end; + +procedure TNativeDataSet.DropFilter(hFilter: hDBIFilter); +var + Count: Integer; +begin + if hFilter = nil then + FFilters.FreeAll + else + begin + Count := FFilters.Count; + FFilters.Delete(hFilter); + if Count <> FFilters.Count then + begin + {$IFDEF NEXTGEN} + TPSQLFilter(hFilter).DisposeOf; + {$ELSE} + TPSQLFilter(hFilter).Free; + {$ENDIF} + UpdateFilterStatus; + end; + end; +end; + +procedure TNativeDataSet.ActivateFilter(hFilter: hDBIFilter); +var + i : Integer; + P : TPSQLFilter; + Found : Boolean; +begin + Found := FALSE; + For i := 0 to FFilters.Count-1 do + begin + P := FFilters.Items[i]; + if (hFilter = nil) or (hFilter = hDBIFilter(P)) then + begin + P.Active := TRUE; + FFilterActive := TRUE; + Found := TRUE; + end; + end; + if not Found and (hFilter <> nil) then raise EPSQLException.CreateBDE(DBIERR_NOSUCHFILTER); +end; + +procedure TNativeDataSet.DeactivateFilter(hFilter: hDBIFilter); +var + i : Integer; + P : TPSQLFilter; +begin + if hFilter = nil then + begin + For i := 0 to FFilters.Count-1 do + begin + P := FFilters.Items[i]; + P.Active := FALSE; + end; + FFilterActive := FALSE; + end else + begin + if TPSQLFilter( hFilter ).Active then + begin + TPSQLFilter( hFilter ).Active := FALSE; + UpdateFilterStatus; + end; + end; +end; + +procedure TNativeDataSet.SetToRecord(RecNo : LongInt); +begin + if RecNo < 0 then + begin + try + if RecordState <> tsEmpty then CurrentRecord(RecNo); + except + end; + end + else + if RecordState <> tsEmpty then CurrentRecord(RecNo); +end; + +procedure TNativeDataSet.SetToBookmark(P : Pointer); +begin + CheckParam(P = nil, DBIERR_INVALIDPARAM); + + if TPSQLBookMark(P^).Position > 0 then + begin + if TPSQLBookMark(P^).Position > RecordCount then + LastRecord() + else + RecordNumber := TPSQLBookMark(P^).Position - 1 + end + else + FirstRecord; + + RecordState := tsPos; +end; + +procedure TNativeDataSet.OpenTable; +var + sql_stmt : string; + + procedure InternalOpen; + begin + if SQLQuery = '' then + if StandartClause.Count > 0 then + sql_stmt := GetSQLClause + else + raise EPSQLException.CreateBDE(DBIERR_QRYEMPTY) + else + sql_stmt := Trim(SQLQuery); + if dsoFetchOnDemand in Options then + if _PQSendQuery(FConnect, sql_stmt) = 1 then + if PQsetSingleRowMode(FConnect.Handle) = 1 then + FStatement := PQgetResult(FConnect.Handle) + else + DatabaseError('Cannot switch to dsoFetchOnDemand mode') + else + FConnect.CheckResult + else + FStatement := _PQExecute(FConnect, sql_stmt); + if Assigned(FStatement) then + begin + try + FConnect.CheckResult(FStatement); + MonitorHook.SQLExecute(Self, True); + except + MonitorHook.SQLExecute(Self, False); + CloseTable; + raise; + end; + FOpen := True; + if FFieldDescs.Count = 0 then + InitFieldDescs; + RecNo := 0; + end else + begin + FConnect.CheckResult; + PQclear(FStatement); + end; + end; + +begin + if FOpen then CloseTable; + FAffectedRows := 0; + FOpen := False; + FFetched := False; + sql_stmt := ''; + + try + if (StandartClause.Count = 0) and (SQLQuery = '') then + begin + isQuery := False; + StandartClause.Add('SELECT * FROM ' + TableName); + if FOpen then ClearIndexInfo; + limitClause.Add('LIMIT 1'); + FSystemNeed := true; + InternalOpen; + FSystemNeed := False; + limitClause.Clear; + if FLimit > 0 then + LimitClause.Add(Format('LIMIT %s',[IntToStr(FLimit)])); + if FOffset > 0 then + LimitClause.Add(Format('OFFSET %s',[IntToStr(FOffset)])); + if IndexCount > 0 then + begin + if FPrimaryKeyNumber = 0 then FPrimaryKeyNumber := 1; + SwitchToIndex(FIndexName, '', 0, False ); + end + else + begin + PQClear(FStatement); + InternalOpen; + end; + Exit; + end; + FLastOperationTime := GetTickCount; + InternalOpen; + FLastOperationTime := GetTickCount - FLastOperationTime; + if (KeyNumber = 0) then + begin + if FPrimaryKeyNumber <> 0 then + GetIndexDesc(FPrimaryKeyNumber, FKeyDesc) + else + if IndexCount > 0 then + if FPrimaryKeyNumber <> 0 then + GetIndexDesc(FPrimaryKeyNumber, FKeyDesc) + else + GetIndexDesc(1, FKeyDesc); + end; + finally + SetLength(FFieldMinSizes,0); + FFieldTypType := ''; + if FSortingFields > '' then + SortBy(FSortingFields); + end; +end; + +procedure TNativeDataSet.GetField(FieldNo: Word;PRecord: Pointer;pDest: Pointer;var bBlank: Boolean); +var + T : TPSQLField; +begin + CheckParam(PRecord = nil, DBIERR_INVALIDPARAM); + T := FFieldDescs[FieldNo]; + T.Buffer := PRecord; + if Assigned(pDest) then + NativeToDelphi(T, PRecord, pDest, bBlank) + else + bBlank := T.FieldNull; +end; + +procedure TNativeDataSet.PutField(FieldNo: Word;PRecord: Pointer;pSrc: Pointer); +var + T : TPSQLField; +begin + CheckParam(PRecord=nil,DBIERR_INVALIDPARAM); + T := FFieldDescs[FieldNo]; + T.Buffer := PRecord; + DelphiToNative(T, PRecord, pSrc); + T.FieldChanged := TRUE; + T.FieldNull := pSrc = nil; +end; + +procedure TNativeDataSet.CloseTable; +begin + FAffectedRows := 0; + RecNo := -1; + if not FConnect.FLoggin then exit; + if FStatement <> nil then PQclear(FStatement); + FStatement := nil; + FOpen := False; + SetLength(FFieldMinSizes,0); + Finalize(FKeyDesc); + FFieldTypType := ''; +end; + +procedure TNativeDataSet.GetBookMark( P : Pointer ); +begin + {$IFNDEF NEXTGEN} + ZeroMemory(P, BookMarkSize ); + {$ELSE} + FillChar(P^, BookMarkSize, 0 ); + {$ENDIF} + with TPSQLBookMark(P^) do + Position := RecordNumber+1; +end; + +procedure TNativeDataSet.GetVchkDesc(iValSeqNo: Word; var pvalDesc: VCHKDesc); +begin + pvalDesc := Fields[iValSeqNo].ValCheck; +end; + +procedure TNativeDataSet.GetCursorProps( var curProps : CURProps ); +begin + {$IFNDEF NEXTGEN} + ZeroMemory(@curProps, SizeOf(curProps)); + {$ELSE} + FillChar(curProps, SizeOf(curProps), 0 ); + {$ENDIF} + With curProps do + begin + iFields := FieldCount; + iRecSize := RecordSize; + iRecBufSize := GetWorkBufferSize; { Record size (physical record) } + iValChecks := FieldCount; + iBookMarkSize := BookMarkSize; { Bookmark size } + bBookMarkStable := False; { Stable book marks } + eOpenMode := FOMode; { ReadOnly / RW } + iSeqNums := 1; { 1: Has Seqnums; 0: Has Record# } + exltMode := xltNONE; { Translate Mode } + bUniDirectional := True; { Cursor is uni-directional } + iFilters := FFilters.Count; { Number of Filters } + if isQuery then + begin + iIndexes := 0; + iKeySize := 0; + end else + begin + iIndexes := IndexCount; + iKeySize := FKeyDesc.iKeyLen; { Key size } + end; + bSoftDeletes := False; + end; +end; + +procedure TNativeDataSet.GetFieldDescs(var pFDesc : TFLDDescList); +var + i : Integer; +begin + for i := Low(pFDesc) to High(pFDesc) do + pFDesc[i] := Fields[i+1].Description; +end; + +procedure TNativeDataSet.Execute; +begin + if FOpen then CloseTable; + FAffectedRows := 0; + FStatement := nil; + if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; + FLastOperationTime := GetTickCount; + FStatement := _PQExecute(FConnect, SQLQuery); + if FStatement <> nil then + begin + try + FConnect.CheckResult(FStatement); + MonitorHook.SQLExecute(Self, True); + except + MonitorHook.SQLExecute(Self, False); + CloseTable; + raise; + end; + FAffectedRows := StrToIntDef(String(PQcmdTuples(FStatement)), 0); + FLastOperationTime := GetTickCount - FLastOperationTime; + PQclear(FStatement); + FStatement := nil; + end else + FConnect.CheckResult; + SQLQuery := ''; +end; + +function TNativeDataset.FieldCount: Integer; +begin + if FStatement = nil then Result := 0 + else Result := PQnfields(FStatement); +end; + +function TNativeDataSet.GetRecordSize: Integer; +var + I, Size: Integer; +begin + Size := 0; + Result := 0; + if FRecSize = -1 then + begin + if FStatement = nil then exit; + + for I := 1 to FieldCount do + Inc(Size, Fields[i].NativeSize); + + Inc(Size, FieldCount); + + FRecSize := Size; + Result := Size; + end + else + Result := FRecSize; +end; + +function TNativeDataSet.FieldName(FieldNum: Integer): String; +begin + Result := ''; + if FStatement <> nil then + Result := FConnect.RawToString(PQfname(FStatement, FieldNum)); +end; + +function TNativeDataSet.FieldIndex(FieldName: String): Integer; +var + P: PAnsiDACChar; +begin + Result := -1; + if FStatement <> nil then + begin + P := FConnect.StringToRaw(FieldName); + try + Result := PQfnumber(FStatement, P); + finally + DACAnsiStrDispose(P); + end; + end; +end; + +function TNativeDataSet.FieldSize(FieldNum: Integer): Integer; +begin + Result := 0; + if (FStatement <> nil) and (PQntuples(FStatement) > 0) then + Result := PQgetlength(FStatement, GetRecNo, FieldNum); +end; + +function TNativeDataSet.FieldMaxSizeInBytes(FieldNum: Integer): Integer; +var FT: cardinal; +begin + FT := FieldType(FieldNum); + case FT of + FIELD_TYPE_BOOL, + FIELD_TYPE_INT2: Result := SizeOf(Smallint); + + FIELD_TYPE_INT4: Result := SizeOf(Integer); + + FIELD_TYPE_INT8: Result := SizeOf(Int64); + + FIELD_TYPE_DATE, + FIELD_TYPE_TIME, + FIELD_TYPE_TIMESTAMP: Result := SizeOf(TDateTime); + + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: Result := Sizeof(Double); + + FIELD_TYPE_NUMERIC: Result := SizeOf(TBcd); + +{$IFDEF DELPHI_12} + FIELD_TYPE_POINT: Result := SizeOf(TPSQLPoint); + FIELD_TYPE_CIRCLE: Result := SizeOf(TPSQLCircle); + FIELD_TYPE_BOX: Result := SizeOf(TPSQLBox); + FIELD_TYPE_LSEG: Result := SizeOf(TPSQLLSeg); + + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: Result := SizeOf(TPSQLRange); +{$ENDIF DELPHI_12} + + FIELD_TYPE_TEXT, + FIELD_TYPE_BYTEA, + FIELD_TYPE_OID: Result := SizeOf(TBlobItem); + FIELD_TYPE_UUID: Result := UUIDLEN + 1; + else + begin + case FT of + FIELD_TYPE_UNKNOWN: Result := NAMEDATALEN; + FIELD_TYPE_INET, FIELD_TYPE_CIDR: Result := INETLEN; + FIELD_TYPE_MACADDR: Result := MACADDRLEN; + FIELD_TYPE_TIMESTAMPTZ: Result := TIMESTAMPTZLEN; + FIELD_TYPE_TIMETZ: Result := TIMETZLEN; + FIELD_TYPE_NAME: Result := NAMEDATALEN; + else + Result := FieldMaxSize(FieldNum); + end; + {$IFDEF DELPHI_12} + if FConnect.IsUnicodeUsed then + Result := (Result + 1 )* SizeOf(Char) //we need two #0 bytes here 25.11.2008 + else + {$ENDIF} + Result := Result + 1; + end; + end; +end; + +function TNativeDataSet.GetFieldTypType(Index: integer): AnsiDACChar; +var fCount: integer; + fTypType: string; + fTypeOid: oid; + IsOK: boolean; +begin + fCount := FieldCount(); + if (Length(FFieldTypType) < fCount) then + FFieldTypType := StringOfChar(AnsiDACChar('u'), fCount); //unknown + if FFieldTypType[Index + 1] = 'u' then + begin + fTypeOid := FieldType(Index); + if fTypeOid < MAX_BUILTIN_TYPE_OID then + FFieldTypType[Index + 1] := 'b' //base + else + begin + fTypType := FConnect.SelectStringDirect('SELECT typtype FROM pg_catalog.pg_type WHERE oid = ' + UIntToStr(fTypeOid), IsOK, 0); + if fTypType > '' then + FFieldTypType[Index + 1] := AnsiDACChar(fTypType[1]) + else + FFieldTypType[Index + 1] := 'X'; //failed to obtain + end; + end; + Result := FFieldTypType[Index + 1]; +end; + +function TNativeDataSet.FieldMaxSize(FieldNum: Integer): Integer; +Var fMod: integer; + fTypeOid: oid; +begin + Result := 0; + if FStatement <> nil then + begin + fMod := Max(PQfmod(FStatement, FieldNum), 0); + fTypeOid := FieldType(FieldNum); + case fTypeOid of + FIELD_TYPE_BPCHAR, + FIELD_TYPE_VARCHAR: Result := (fMod - 4); + FIELD_TYPE_BIT, + FIELD_TYPE_VARBIT: Result := fMod; + + FIELD_TYPE_NUMERIC: Result := fMod; // shr 16 and 65535 + 1; //frac delimiter + else + if fTypeOid > MAX_BUILTIN_TYPE_OID then //suppose it's UDT or enum + case FieldTypTypes[FieldNum] of //we're interested in composites & enums only + 'c': ;//composite TODO + 'e': Result := NAMEDATALEN; //enum + end; + end; + if Result <= 0 then + Result := FieldMinSize(FieldNum); + end; +end; + +function TNativeDataSet.FieldMinSize(FieldNum: Integer): Integer; +var + I, H: Integer; +begin + if dsoUDTAsMaxString in FOptions then + begin + Result := MAX_CHAR_LEN; + Exit; + end + else + Result := 0; + if not Assigned(FFieldMinSizes) or + (High(FFieldMinSizes) < FieldNum) or + (FFieldMinSizes[FieldNum] = -1) + then + begin + if Assigned(FFieldMinSizes) then + H := High(FFieldMinSizes) + 1 + else + H := 0; + SetLength(FFieldMinSizes, FieldNum + 1); + for i := H to High(FFieldMinSizes) - 1 do + FFieldMinSizes[i] := -1; + if FStatement <> nil then + for I := 0 to PQntuples(FStatement) - 1 do + if PQgetlength(FStatement, I, FieldNum) > Result then + Result := PQgetlength(FStatement, I, FieldNum); + if Result = 0 then + Result := MAX_CHAR_LEN; //there is no field of length 0 + FFieldMinSizes[FieldNum] := Result; + end + else + Result := FFieldMinSizes[FieldNum]; +end; + +function TNativeDataSet.FieldType(FieldNum: Integer): cardinal; +begin + Result := InvalidOid; + if FStatement <> nil then + Result := PQftype(FStatement, FieldNum); + case Result of + FIELD_TYPE_OID: if dsoOIDAsInt in FOptions then + Result := FIELD_TYPE_INT8; + FIELD_TYPE_BYTEA: if dsoByteaAsEscString in FOptions then + Result := FIELD_TYPE_TEXT; + FIELD_TYPE_NUMERIC: +{$IFDEF DELPHI_12} + if dsoNumericAsFloat in FOptions then +{$ENDIF} + Result := FIELD_TYPE_FLOAT8; + FIELD_TYPE_OIDVECTOR: Result := FIELD_TYPE_VARCHAR; + FIELD_TYPE_CID, + FIELD_TYPE_XID, + FIELD_TYPE_TID: Result := FIELD_TYPE_INT4; + else + if (Result = FIELD_TYPE_VARCHAR) AND + ((PQfmod(FStatement, FieldNum) < 0) or (PQfmod(FStatement, FieldNum) > MAX_CHAR_LEN)) + or + (Result = FIELD_TYPE_XML) then + Result := FIELD_TYPE_TEXT; //added to deal with varchar without length specifier + end; +end; + +function TNativeDataSet.FieldTypMod(FieldNum: Integer): Integer; +begin + Result := -1; + if Assigned(FStatement) then + Result := PQfmod(FStatement, FieldNum); +end; + +function TNativeDataSet.FetchRecords(const NumberOfRecs: integer = 1): integer; +var + LocResult: PPGResult; + i: integer; + fval: PAnsiDACChar; + flen: Integer; + CurrentRecNum: Integer; +const + NULL_LEN: integer = -1; +begin + Result := 0; + repeat + LocResult := PQgetResult(FConnect.Handle); + FFetched := not Assigned(LocResult); + case PQresultStatus(LocResult) of + PGRES_SINGLE_TUPLE: + begin + CurrentRecNum := PQntuples(FStatement); + for i := 0 to PQnfields(LocResult) - 1 do + begin + if PQgetisnull(LocResult, 0, i) = 1 then + flen := NULL_LEN + else + flen := PQgetlength(LocResult, 0, i); + fval := PQgetvalue(LocResult, 0, i); + if PQsetvalue(FStatement, CurrentRecNum, i, fval, flen) = 0 then + raise EPSQLException.CreateFmt('Cannot consume row on demand. Operation for field "%s" failed', [PQfname(LocResult, i)]); + end; + PQClear(LocResult); + inc(Result); + end; + PGRES_TUPLES_OK: + begin + LocResult := PQgetResult(FConnect.Handle); + FFetched := not Assigned(LocResult); + end; + else + Exit; //no rows for fetching, command returning no data executed? + end; + until (Result = NumberOfRecs) or FFetched; +end; + +function TNativeDataSet.Field(FieldNum: Integer): string; +begin + Result := ''; + if FStatement = nil then Exit; + Result := FConnect.RawToString(PQgetvalue(FStatement,GetRecNo,FieldNum)); + if Fieldtype(FieldNum) = FIELD_TYPE_BPCHAR then + Result := TrimRight(Result); +end; + +function TNativeDataSet.FieldIsNull(FieldNum: Integer): Boolean; +begin + Result := true; + if FStatement <> nil then + Result := PQgetisnull(FStatement,GetRecNo,FieldNum) <> 0; +end; + +function TNativeDataSet.FieldBuffer(FieldNum: Integer): PAnsiDACChar; +begin + Result := nil; + if (FStatement = nil) or (PQgetisnull(FStatement, GetRecNo, FieldNum) <> 0) then Exit; + Result := PQgetvalue(FStatement, GetRecNo, FieldNum); +end; + +procedure TNativeDataSet.GetNativeDesc(FieldNo : Integer; var P : FldDesc; var P1 : VCHKDesc; Var LocType, LocSize : Integer; var LocArray : Boolean); +var + Fld : TPGFIELD_INFO; +begin + CheckParam(not (FieldNo <= FieldCount), DBIERR_INVALIDRECSTRUCT); + FLD := FieldInfo[FieldNo-1]; + ConverPSQLtoDelphiFieldInfo(FLD, FieldNo, FieldOffset(FieldNo), P, P1, LocArray); + LocType := FieldType(FieldNo-1); + case Loctype of + FIELD_TYPE_BYTEA, + FIELD_TYPE_TEXT, + FIELD_TYPE_OID: LocSize := SizeOf(TBlobItem); + else + LocSize := FieldMaxSizeInBytes(FieldNo-1); + end; +end; + +function TNativeDataSet.GetFieldInfo(Index : Integer) : TPGFIELD_INFO; +var + Item : TPSQLNative; + I : Integer; + + procedure FillDefsAndNotNulls(); + Var inS: String; + i, j, fPos: integer; + tabOID: cardinal; + RES: PPGresult; + sql: String; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} + const + tS = ' c.oid = %d AND a.attnum = %d '; + + begin + if IsQuery and (FOMode = dbiReadOnly) then Exit; + sql := 'SELECT a.attnum, '#13#10 + + ' c.oid, '#13#10 + + 'CASE WHEN a.attnotnull OR t.typtype = ''d''::"char" AND t.typnotnull '#13#10 + + ' THEN FALSE ELSE TRUE END AS is_nullable, '#13#10+ + 'pg_get_expr(ad.adbin, ad.adrelid) '#13#10 + + 'FROM pg_attribute a LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum, '#13#10 + + 'pg_class c, pg_type t '#13#10 + + 'WHERE a.atttypid = t.oid AND a.attrelid = c.oid '#13#10 + + 'AND a.attnum > 0 AND not a.attisdropped '#13#10 + + 'AND (%s) '#13#10 + + 'ORDER BY a.attnum'; + + if not isQuery then + inS := ' c.oid = ' + IntToStr(FieldTable(0)) + else + for i:=0 to FieldCount-1 do + begin + tabOID := FieldTable(I); + fPos := FieldPosInTable(I); + if (tabOID > InvalidOid) and (fPos > -1) then + if inS > '' then + inS := inS + 'OR' + Format(ts,[tabOID,fPos]) + else + inS := Format(ts,[tabOID,fPos]); + end; + if inS > '' then + begin + sql := Format(sql, [inS]); + Res := PQExec(FConnect.Handle, {$IFNDEF NEXTGEN} + PAnsiChar(AnsiString(sql)) + {$ELSE} + M.AsAnsi(sql).ToPointer + {$ENDIF}); + if Assigned(RES) then + try + FConnect.CheckResult; + for i := 0 to PQntuples(RES) - 1 do + for j := 0 to FieldCount - 1 do + if (IntToStr(FieldTable(j)) = FConnect.RawToString(PQGetValue(Res, i, 1))) and + (IntToStr(FieldPosInTable(j)) = FConnect.RawToString(PQGetValue(Res, i, 0))) then + with TPSQLNative(FNativeDescs.Items[j]) do + begin + NativeNotNull := FConnect.RawToString(PQgetvalue(RES, i, 2)) = 'f'; + NativeDefault := FConnect.RawToString(PQgetvalue(RES, i, 3)); + end; + finally + PQclear(RES); + end; + end; + end; + +begin + if FNativeDescs.Count = 0 then + begin + for I := 0 to FieldCount - 1 do + FNativeDescs.SetNative(I, FieldName(I), FieldType(I), FieldMaxSizeInBytes(I), FieldMaxSize(I), FieldTypMod(I)); + FillDefsAndNotNulls(); + end; + Item := TPSQLNative(FNativeDescs.Items[Index]); + if Item <> nil then + Result := Item.FDesc; +end; + +procedure TNativeDataSet.InitFieldDescs; +var + i : Integer; + FldInfo : FLDDesc; + ValCheck : VCHKDesc; + LocalType, LocalSize : Integer; + RecSize, NullOffset: Integer; + LocArray : Boolean; +begin + Fields.Clear; + for i := 1 to FieldCount() do + begin + GetNativeDesc(i, FldInfo, ValCheck, LocalType, LocalSize, LocArray); + Fields.AddField(FldInfo, ValCheck, i, LocalType, LocalSize, LocArray); + Finalize(FldInfo); //without this calls we have memory leak + Finalize(ValCheck); + end; + + RecSize := RecordSize; + NullOffset := RecSize + 1; + for i := 1 to Fields.Count do + begin + Fields[i].NullOffset := NullOffset; + Inc(NullOffset, SizeOf(TFieldStatus)); + end; +end; + +function TNativeDataSet.GetBufferSize : integer; +begin + if FFieldDescs.Count = 0 then InitFieldDescs; + Result := RecordSize; +end; + +function TNativeDataSet.GetWorkBufferSize : integer; +begin + Result := GetBufferSize; + Inc(Result, FFieldDescs.Count * SizeOf(TFieldStatus) + 1); + FBookOfs := Result; + if FBookOfs > 0 then Inc(Result, BookMarkSize); +end; + +procedure TNativeDataSet.GetProp(iProp: integer;PropValue: Pointer;iMaxLen: integer; var iLen: integer); +begin + iLen := 0; + Case TPropRec( iProp ).Prop of + Word( curMAXPROPS ): begin + iLen := SizeOf(Word); + Word(PropValue^) := maxCurProps; + end; + Word( curXLTMODE ): begin + iLen := SizeOf(xltMODE); + xltMODE( PropValue^ ) := xltNONE; + end; + Word(curMAXFIELDID): begin + iLen := iMaxLen; + Integer( PropValue^ ) := FFieldDescs.Count; + end; + Word(stmtROWCOUNT): begin + iLen := SizeOf(Integer); + Integer(PropValue^) := FAffectedRows; + end; + Word(curAUTOREFETCH):begin + iLen := SizeOf(Boolean); + Boolean(PropValue^) := FReFetch; + end; + end; +end; + +procedure TNativeDataSet.SetProp(iProp: integer;PropValue: Longint); +begin + Case TPropRec( iProp ).Prop of + Word(curMAKECRACK): RecordState := tsEmpty; + Word(stmtLIVENESS): begin + if PropValue = 1 then + FOMode := dbiReadWrite else + FOMode := dbiREADONLY; + end; + Word(curAUTOREFETCH): FReFetch := PropValue > 0; + end; +end; + +procedure TNativeDataSet.SetToBegin; +begin + RecordState := tsFirst; +end; + +procedure TNativeDataSet.SetToEnd; +begin + RecordState := tsLast; +end; + + +procedure TNativeDataSet.InternalReadBuffer; +var + i, size: Integer; + null: boolean; //temp var used for work with dsoEmptyCharAsNull + MaxSize, tMS : Integer; + T: TPSQLField; + origBuffer: Pointer; + FldValue : String; + Data : pointer; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if Assigned(FCurrentBuffer) then + begin + MaxSize := 0; + for i := 0 to FieldCount - 1 do + case FieldType(I) of + FIELD_TYPE_OID, FIELD_TYPE_TEXT, FIELD_TYPE_BYTEA: //ignore + else + begin + tMS := FieldMaxSizeInBytes(I); + if tMS > MaxSize then MaxSize := tMS; + end; + end; + GetMem(Data, MaxSize + 1); + origBuffer := FCurrentBuffer; + for i := 0 to FieldCount - 1 do + begin + T := Fields[i+1]; + T.Buffer := origBuffer; + T.FieldChanged := FALSE; + null := FieldIsNull(I); + T.FieldNull := null; + size := T.NativeSize; //FieldLength + if null then + {$IFNDEF NEXTGEN} + ZeroMemory(FCurrentBuffer,size) + {$ELSE} + FillChar(FCurrentBuffer^, size, 0) + {$ENDIF} + else + begin + if (T.NativeType <> FIELD_TYPE_OID) and + (T.NativeType <> FIELD_TYPE_TEXT) and + (T.NativeType <> FIELD_TYPE_BYTEA) + then + FldValue := FConnect.RawToString(FieldBuffer(i)); + case T.NativeType of + FIELD_TYPE_INT2: SmallInt(Data^) := SmallInt(StrToint(FldValue)); + FIELD_TYPE_BOOL: if FldValue = 't' then SmallInt(Data^) := 1 else SmallInt(Data^) := 0; + FIELD_TYPE_INT4: LongInt(Data^) := LongInt(StrToint(FldValue)); + FIELD_TYPE_INT8: Int64(Data^) := StrToInt64(FldValue); + FIELD_TYPE_DATE: TDateTime(Data^) := SQLDateToDateTime(FldValue, False); + FIELD_TYPE_TIME: TDateTime(Data^) := SQLDateToDateTime(FldValue, True); + FIELD_TYPE_TIMESTAMP: TDateTime(Data^) := SQLTimeStampToDateTime(FldValue); +{$IFDEF UNDER_DELPHI_12} + FIELD_TYPE_NUMERIC, +{$ENDIF} + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: Double(Data^) := StrToSQLFloat(FldValue); +{$IFDEF DELPHI_12} + FIELD_TYPE_NUMERIC: TBcd(Data^) := StrToBcd(FldValue, PSQL_FS); + FIELD_TYPE_POINT: TPSQLPoint(Data^) := SQLPointToPoint(FldValue); + FIELD_TYPE_CIRCLE: TPSQLCircle(Data^) := SQLCircleToCircle(FldValue); + FIELD_TYPE_BOX: TPSQLBox(Data^) := SQLBoxToBox(FldValue); + FIELD_TYPE_LSEG: TPSQLLSeg(Data^) := SQLLSegToLSeg(FldValue); + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: TPSQLRange(Data^) := SQLRangeToRange(FldValue, T.NativeType); +{$ENDIF DELPHI_12} + FIELD_TYPE_OID, + FIELD_TYPE_TEXT, + FIELD_TYPE_BYTEA: begin + size := SizeOf(TBlobItem); + {$IFNDEF NEXTGEN} + ZeroMemory(FCurrentBuffer, Size); + {$ELSE} + FillChar(FCurrentBuffer^, Size, 0); + {$ENDIF} + Inc(PAnsiDACChar(FCurrentBuffer)); //Null byte allocate + Inc(PAnsiDACChar(FCurrentBuffer), Size); //Pointer allocate + Continue; + end; + {$IFNDEF NEXTGEN} + FIELD_TYPE_UUID: {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrCopy(PAnsiChar(Data), PAnsiChar(BadGuidToGuid(AnsiString(FldValue)))); + {$ELSE} + FIELD_TYPE_UUID: DACStrCopy(PAnsiDACChar(Data), M.AsAnsi(BadGuidToGuid(FldValue)).ToPointer); + {$ENDIF} + else + if dsoTrimCharFields in FOptions then + FldValue := TrimRight(FldValue); + if FConnect.IsUnicodeUsed then + {$IFDEF DELPHI_12} + StrCopy(PWideChar(Data), PWideChar(FldValue)) + {$ELSE} + StrCopy(PAnsiChar(Data), PAnsiChar(FldValue)) + {$ENDIF} + else + {$IFNDEF NEXTGEN} + {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrCopy(PAnsiChar(Data), PAnsiChar(AnsiString(FldValue))); + {$ELSE} + DACStrCopy(PAnsiDACChar(Data), M.AsAnsi(FldValue).ToPointer); + {$ENDIF} + end; + Move(Data^, (PAnsiDACChar(FCurrentBuffer) + 1)^, Size); + PAnsiDACChar(FCurrentBuffer)^ := #1; {null indicator 1=Data 0=null} + end; + Inc(PAnsiDACChar(FCurrentBuffer), Size + 1); {plus 1 for null byte} + end; + FreeMem(Data, MaxSize + 1); + FCurrentBuffer := nil; + end; +end; + +procedure TNativeDataSet.ReadBlock(var iRecords : integer; pBuf : Pointer); +var + M : MemPtr; + i : integer; + Limit : longint; +begin + Limit := iRecords; + iRecords := 0; + CheckParam(pBuf = nil, DBIERR_INVALIDPARAM); + M := pBuf; + i := 0; + repeat + GetNextRecord(dbiNOLOCK, @M^[ i ], NIL); + Inc(iRecords); + if iRecords >= Limit then + Break + else + Inc(i, GetWorkBufferSize); + until False; +end; + +procedure TNativeDataSet.ForceReread; +var + P : TPSQLBookMark; +begin + GetBookMark(@P); + FReRead := TRUE; + ReOpenTable; + if RecordCount > 0 then + SetToBookmark(@P); +end; + +procedure TNativeDataSet.CompareBookMarks( pBookMark1, pBookMark2 : Pointer; var CmpBkmkResult : CmpBkmkRslt ); + + function cmp2Values(val1, val2: LongInt): CmpBkmkRslt; + begin + if val1 = val2 then result := CMPEql else if val1 < val2 then result := CMPLess else result := CMPGtr; + end; + +begin + CheckParam(pBookMark1 = nil,DBIERR_INVALIDPARAM); + CheckParam(pBookMark2 = nil,DBIERR_INVALIDPARAM); + if (TPSQLBookMark(pBookMark1^).Position <> 0) then + CmpBkMkResult := cmp2Values( TPSQLBookMark(pBookMark1^).Position, TPSQLBookMark(pBookMark2^).Position) else + CmpBkMkResult := CMPGtr; +end; + +procedure TNativeDataSet.InitRecord(PRecord : Pointer); +begin + if PRecord = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDPARAM); + {$IFNDEF NEXTGEN} + ZeroMemory(PRecord, GetWorkBufferSize); + {$ELSE} + FillChar(PRecord^, GetWorkBufferSize, 0); + {$ENDIF} + FFieldDescs.SetFields(PRecord); + CurrentBuffer := PRecord; +end; + + +procedure TNativeDataSet.GetKeys(Unique: Boolean; var FieldList: TFieldArray; var FieldCount: Integer); +var + I, N: Integer; + Item : TPSQLIndex; + Fld : TPSQLField; +begin + N := -1; + FieldCount := 0; + //Search for PrimaryKey + for I := 1 to FindexDescs.Count do + begin + Item := FIndexDescs.mIndex[I]; + if Item.Primary then + begin + N := I; + Break; + end; + end; + if N = -1 then + //Primary key not found. + //Search for Unique Key + for I := 1 to FindexDescs.Count do + begin + Item := FIndexDescs.mIndex[I]; + if Item.Unique then + begin + N := I; + break; + end; + end; + if N >= 0 then + begin + Item := FindexDescs.mIndex[N]; + for I := 0 to Item.FldsInKey-1 do + begin + FieldList[FieldCount] := Item.FDesc.aiKeyFld[I]; + Inc(FieldCount); + end; + end + else + if not Unique then + begin + for I := 1 to FFieldDescs.Count do + begin + Fld := FFieldDescs.Field[I]; + if not(Fld.FieldType in [fldBlob]) then + begin + if Fld.FDesc.bCalcField then continue; + FieldList[FieldCount] := I; + Inc(FieldCount); + end; + end; + end; +end; + +function TNativeDataSet.GetLOUnlinkSQL(ObjOID: string): string; +const + _LoMng: string = #13#10'SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = %) THEN lo_unlink(%) END;'; +begin + Result := StringReplace(_LoMng, '%', ObjOID, [rfReplaceAll]); +end; + +function TNativeDataSet.GetDeleteSQL(Table: string; PRecord: Pointer): string; +var + AFieldCount, I: Integer; + FieldList : TFieldArray; + Fld : TPSQLField; +begin + Result := ''; + GetKeys(False, FieldList, AFieldCount); + for I := 0 to AFieldCount-1 do + begin + Fld := FFieldDescs.Field[FieldList[I]]; + Fld.Buffer:= PRecord; + if Result <> '' then Result := Result + ' AND '; + Result := Result + AnsiQuotedStr(Fld.FieldName, '"'); + if Fld.FieldNull then + Result := Result + ' IS NULL' + else + Result := Result + '=' + Fld.FieldValueAsStr; + end; + if Result = '' then Exit; + Result := 'DELETE FROM ' + Table + ' WHERE ' + Result; + + if not (dsoManageLOFields in FOptions) then Exit; + for I := 1 to FFieldDescs.Count do + begin + Fld := FFieldDescs.Field[I]; + if (Fld.NativeBLOBType = nbtOID) and not FieldIsNull(I-1) then + Result := GetLOUnlinkSQL(Field(I-1)) + #13#10 + Result; + end; +end; + +procedure TNativeDataSet.FreeBlobStreams(PRecord: Pointer); +var + I : Integer; +begin + for I := 1 to FFieldDescs.Count do + if FFieldDescs.Field[I].FieldType = fldBLOB then + FreeBlob(PRecord,i); +end; + +function TNativeDataSet.GetInsertSQL(Table: string; PRecord: Pointer; ReturnUpdated: boolean = False): string; +var + I : Integer; + Fld : TPSQLField; + SFields : String; + Values : String; + + function GetRETURNING: string; + var I: integer; + begin + Result := ''; + if not ReturnUpdated then Exit; + if FieldCount > 0 then Result := ' RETURNING ' + AnsiQuotedStr(FieldName(0), '"'); + for I := 1 to FieldCount-1 do + Result := Result + ', ' + AnsiQuotedStr(FieldName(I), '"'); + end; + +begin + Result := ''; + SFields := ''; + for I := 1 to FFieldDescs.Count do + begin + Fld := FFieldDescs.Field[I]; + Fld.Buffer:= PRecord; + if (Fld.FieldNull) and (not Fld.FValCheck.bHasDefVal) then continue; + SFields := SFields + AnsiQuotedStr(Fld.FieldName, '"') + ', '; + if (Fld.FieldNull) and (Fld.FValCheck.bHasDefVal) then + Values := Values + 'DEFAULT, ' + else + Values := Values + Fld.FieldValueAsStr + ', '; + end; + Delete(SFields, Length(SFields)-1, 2); + Delete(Values, Length(Values)-1, 2); + if (SFields <> '') and (Values <> '') then + Result := Format('INSERT INTO %s (%s) VALUES (%s)', [Table, SFields, Values]) + GetReturning(); +end; + +function TNativeDataSet.GetUpdateSQL(Table: string; OldRecord,PRecord: Pointer; ReturnUpdated: boolean = False): String; +var + I: Integer; + Fld: TPSQLField; + FldName, Values: string; + + function GetWHERE(P : Pointer) : String; + var + I, AFieldCount: Integer; + FieldList : TFieldArray; + Fld : TPSQLField; + FldName: string; + begin + Result := ''; + GetKeys(False, FieldList, AFieldCount); + for I := 0 to AFieldCount-1 do + begin + Fld := FFieldDescs.Field[FieldList[I]]; + Fld.Buffer:= P; + FldName := AnsiQuotedStr(Fld.FieldName, '"'); + if Result <> '' then Result := Result + ' AND '; + if Fld.FieldNull then + Result := Result + FldName + ' IS NULL' + else + Result := Result + FldName + '=' + Fld.FieldValueAsStr; + end; + Result := ' WHERE ' + Result; + end; + + function GetRETURNING: string; + var I: integer; + begin + Result := ''; + if not ReturnUpdated then Exit; + if FieldCount > 0 then Result := ' RETURNING ' + AnsiQuotedStr(FieldName(0), '"'); + for I := 1 to FieldCount-1 do + Result := Result + ', ' + AnsiQuotedStr(FieldName(I), '"'); + end; + +begin + Result := ''; + for I := 1 to FFieldDescs.Count do + begin + Fld := FFieldDescs.Field[I]; + Fld.Buffer:= PRecord; + if not Fld.FieldChanged then Continue; + if (dsoManageLOFields in FOptions) and (Fld.NativeBLOBType = nbtOID) and not FieldIsNull(I-1) then + Result := Result + GetLOUnlinkSQL(Field(I-1)); + FldName := AnsiQuotedStr(Fld.FieldName, '"'); + if Fld.FieldNull then + Values := Values + FldName + '=NULL, ' + else + Values := Values + FldName + '=' + Fld.FieldValueAsStr + ', '; + end; + Delete(VALUES,Length(Values)-1,2); + if VALUES > '' then + Result := Format('UPDATE %s SET %s', [Table, Values]) + GetWhere(OldRecord) + GetRETURNING() + ';' + Result; //LOUnlinkSql at the end +end; + +procedure TNativeDataSet.AppendRecord (PRecord : Pointer); +begin + InsertRecord(dbiNOLOCK, PRecord); +end; + +procedure TNativeDataSet.InsertRecord( eLock : DBILockType; PRecord : Pointer ); +var + SQL : String; + OldQueryFlag: boolean; + KN, i: integer; + AStatement, ATempCopyStmt: PPGResult; + fval, fname: PAnsiDACChar; + flen: integer; + CurrentRecNum: integer; +begin + AStatement := nil; + + KN := -1; + if FOMode = dbiREADONLY then + raise EPSQLException.CreateBDE(DBIERR_TABLEREADONLY); + CheckUniqueKey(KN); + + SQL := GetInsertSQL(TableName, PRecord, dsoRefreshModifiedRecordOnly in FOptions); + try + AStatement := _PQExecute(FConnect, SQL); + if Assigned(AStatement) then + try + FConnect.CheckResult(AStatement); + MonitorHook.SQLExecute(Self, True); + FAffectedRows := StrToIntDef(string(PQcmdTuples(AStatement)), 0); + CurrentRecNum := PQntuples(FStatement); + if (FAffectedRows > 0) and (dsoRefreshModifiedRecordOnly in Options) then + begin + ATempCopyStmt := PQcopyResult(FStatement, PG_COPYRES_TUPLES); //hack because libpq have some bugs. must be eliminated further + if not Assigned(ATempCopyStmt) then + raise EPSQLException.CreateMsg(FConnect, 'Refresh for inserted fiels failed, cannot copy results'); + for i := 0 to PQnfields(AStatement) - 1 do + begin + fname := PQfname(AStatement, i); + if PQgetisnull(AStatement, 0, i) = 1 then + begin + fval := nil; + flen := -1; + end else + begin + fval := PQgetvalue(AStatement, 0, i); + flen := PQgetlength(AStatement, 0, i); + end; + if PQsetvalue(ATempCopyStmt, CurrentRecNum, i, fval, flen) = 0 then + raise EPSQLException.CreateFmt('Refresh for inserted fiels "%s" failed', [fname]); + end; + PQclear(FStatement); + FStatement := ATempCopyStmt; + RecordState := tsPos; + SettoSeqNo(RecordCount); //tuple added to the end + FReFetch := False; + end; + except + MonitorHook.SQLExecute(Self, False); + raise; + end + else + FConnect.CheckResult; + finally + PQclear(AStatement); + end; + + FreeBlobStreams(PRecord); + InternalBuffer := nil; + if (FAffectedRows > 0) and not (dsoRefreshModifiedRecordOnly in Options) then + if not FReFetch then + begin + OldQueryFlag := IsQuery; + ReOpenTable; + IsQuery := OldQueryFlag; + RecordState := tsPos; + try + if not SetRowPosition(KN, GetLastInsertID(KN), PRecord) then + SettoSeqNo(RecordCount); + except + end; + end; + FIsLocked := FALSE; +end; + +procedure TNativeDataSet.ModifyRecord(OldRecord,PRecord : Pointer; bFreeLock : Boolean; ARecNo : Longint); +var + SQL : String; + OldQueryFlag: boolean; + KN : Integer; + i: integer; + CurrentRecNum: LongInt; + AStatement: PPGResult; + fval, fname: PAnsiDACChar; + flen: integer; +begin + KN := -1; + AStatement := nil; + CurrentRecNum := RecNo; + if FOMode = dbiREADONLY then + raise EPSQLException.CreateBDE(DBIERR_TABLEREADONLY); + CheckUniqueKey(KN); + try + SQL := GetUpdateSQL(TableName, OldRecord, PRecord, dsoRefreshModifiedRecordOnly in FOptions); + if SQL <> '' then + AStatement := _PQExecute(FConnect, SQL); + if Assigned(AStatement) then + try + FConnect.CheckResult(AStatement); + MonitorHook.SQLExecute(Self, True); + FAffectedRows := StrToIntDef(string(PQcmdTuples(AStatement)), 0); + if (FAffectedRows > 0) and (dsoRefreshModifiedRecordOnly in Options) then + for i := 0 to PQnfields(AStatement) - 1 do + begin + fname := PQfname(AStatement, i); + if PQgetisnull(AStatement, 0, i) = 1 then + begin + fval := nil; + flen := -1; + end else + begin + fval := PQgetvalue(AStatement, 0, i); + flen := PQgetlength(AStatement, 0, i); + end; + if PQsetvalue(FStatement, CurrentRecNum, i, fval, flen) = 0 then + raise EPSQLException.CreateFmt('Refresh for modifed fiels "%s" failed', [fname]); + end; + FReFetch := False; + RecordState := tsPos; + except + MonitorHook.SQLExecute(Self, False); + raise; + end + else + FConnect.CheckResult; + finally + PQclear(AStatement); + end; + + FreeBlobStreams(OldRecord); + FreeBlobStreams(PRecord); + InternalBuffer := nil; + if bFreeLock then LockRecord(dbiNOLOCK); + + if (FAffectedRows > 0) and not (dsoRefreshModifiedRecordOnly in Options) then + if not FReFetch then + begin + OldQueryFlag := IsQuery; + ReOpenTable; + IsQuery := OldQueryFlag; + RecordState := tsPos; + try + if not SetRowPosition(KN, 0, PRecord) then + SettoSeqNo(CurrentRecNum + 1); + except + end; + end; + FIsLocked := FALSE; +end; + +procedure TNativeDataSet.DeleteRecord(PRecord : Pointer); +var + SQL : String; + CurrentRecNum: LongInt; + ATempCopyStmt: PPGResult; + fval: PAnsiDACChar; + flen, i, j: integer; +begin + if FOMode = dbiREADONLY then + raise EPSQLException.CreateBDE(DBIERR_TABLEREADONLY); + + InternalBuffer := PRecord; + SQL := GetDeleteSQL(TableName, PRecord); + if Sql <> '' then + begin + FConnect.QExecDirect(SQL, nil, FAffectedRows); + RecordState := tsEmpty; + end; + + FreeBlobStreams(PRecord); + InternalBuffer := nil; + + if (FAffectedRows > 0) and (dsoRefreshModifiedRecordOnly in Options) then + begin + ATempCopyStmt := PQcopyResult(FStatement, PG_COPYRES_ATTRS); //hack because libpq have some bugs. must be eliminated further + if not Assigned(ATempCopyStmt) then + raise EPSQLException.CreateMsg(FConnect, 'Refresh for deleted fiels failed, cannot copy results'); + j := 0; + for CurrentRecNum := 0 to RecordCount - 1 do + begin + if CurrentRecNum = RecNo then + Continue; // exclude row from new set and check bounds + for i := 0 to PQnfields(FStatement) - 1 do + begin + if PQgetisnull(FStatement, CurrentRecNum, i) = 1 then + begin + fval := nil; + flen := -1; + end + else + begin + fval := PQgetvalue(FStatement, CurrentRecNum, i); + flen := PQgetlength(FStatement, CurrentRecNum, i); + end; + if PQsetvalue(ATempCopyStmt, J, i, fval, flen) = 0 then + raise EPSQLException.CreateFmt('Refresh for deleted fiels failed', []); + end; + INC(J); + end; + PQclear(FStatement); + FStatement := ATempCopyStmt; + RecordState := tsPos; + try + SettoSeqNo(Min(RecNo + 1, RecordCount)); + except + // + end; + end; + + if (FAffectedRows > 0) and not (dsoRefreshModifiedRecordOnly in Options) then + if not FReFetch then + begin + CurrentRecNum := RecNo; + ReOpenTable; + RecordState := tsPos; + if CurrentRecNum >= RecordCount then + CurrentRecNum := RecordCount; + try + SettoSeqNo(CurrentRecNum); + except + end; + end; + FIsLocked := FALSE; +end; + +function TNativeDataSet.GetTableName : string; +var IsOK: boolean; + s: string; +begin + Result := FTablename; + if (Length(Result) = 0) and (FOMode <> dbiREADONLY) then + begin + s := Format('SELECT %u::regclass',[FieldTable(0)]); + Result := FConnect.SelectStringDirect(PChar(s),IsOK,0); + if IsOK then + FTableName := Result; + end; +end; + +procedure TNativeDataSet.SetTableName(Name : string); +begin + FTableName := Name; +end; + +function TNativeDataSet.GetSQLClause: string; +begin + Result := StandartClause.Text + RangeClause.Text + OrderClause.Text + LimitClause.Text +end; + +function TNativeDataSet.GetIndexCount : Integer; +var + i: Integer; + ATableOID: cardinal; + aPrim,aUniq,aSort : Boolean; + Buffer : String; + J : Integer; + LastIdx: integer; + sSQLQuery: string; + RES: PPGresult; + IdxName: string; +begin + Result := 0; + if not FIndexDescs.Updated then + begin + if isQuery and (FOMode = dbiReadOnly) or (FieldCount <= 0) + then Exit; //multitable or non-Select SQL query + + ATableOID := FieldTable(0); + sSqlQuery := 'SELECT t1.relname AS name,'#13#10+ + ' i.indisunique as "unique",'#13#10+ + ' i.indkey as fields,'#13#10+ + ' i.indisprimary'#13#10+ + ' FROM "pg_index" as i, "pg_class" as t1, "pg_class" as t2'#13#10+ + ' WHERE i.indexrelid = t1.oid'#13#10+ + ' AND i.indrelid = t2.oid'#13#10+ + ' AND t2.oid = %u'#13#10+ + ' AND i.indexprs IS NULL'#13#10; + sSQLQuery := Format(sSQLQuery, [ATableOID] ); + + Res := _PQExecute(FConnect, sSQLQuery); + if Assigned(RES) then + try + FConnect.CheckResult; + for i := 0 to PQntuples(RES) - 1 do + begin + aUniq := PQgetvalue(Res,i,1) = 't'; + aPrim := PQgetvalue(Res,i,3) = 't'; + aSort := False; + Buffer := FConnect.RawToString(PQgetvalue(Res,i,2)); + for J :=1 to Length(Buffer) do + if Buffer[J] = ' ' then Buffer[J] := ','; + IdxName := FConnect.RawToString(PQgetvalue(Res,i,0)); + LastIdx := FIndexDescs.SetIndex(IdxName, Buffer, aPrim, aUniq, aSort); + if LastIdx > 0 then + if aPrim and (FPrimaryKeyNumber = 0) then + FPrimaryKeyNumber := LastIdx; + end; + FIndexDescs.Updated := True; + finally + PQclear(RES); + end; + end; + Result := FIndexDescs.Count; +end; + +procedure TNativeDataSet.OpenBlob(PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode); +var + AField : TPSQLField; + Mode : Integer; +begin + if eOpenMode = dbiREADONLY then Mode := INV_READ else Mode := INV_WRITE; + AField := Fields[FieldNo]; + CheckParam(AField.FieldType <> fldBLOB,DBIERR_NOTABLOB); + if AField.NativeBLOBType = nbtOID then //make sure we have deal with lo_xxx + if FieldBuffer(FieldNo-1) <> nil then + begin + FBlobHandle := StrToUInt(Self.Field(FieldNo-1)); + if FBlobHandle <> InvalidOID then + begin + FConnect.BeginBLOBTran; + FLocalBHandle := lo_open(Fconnect.Handle, FBlobHandle, Mode); + if FLocalBHandle >= 0 then + FBlobOpen := True + else + FConnect.RollbackBLOBTran; //17.08.2009 + end; + end; +end; + +procedure TNativeDataSet.CloseBlob(FieldNo: Word); +var + AField : TPSQLField; +begin + AField := Fields[FieldNo]; + CheckParam(AField.FieldType <> fldBLOB, DBIERR_NOTABLOB); + if FBlobOpen and (AField.NativeBLOBType = nbtOID) and (FLocalBHandle >= 0) then + begin + lo_close(FConnect.Handle, FLocalBHandle); + FConnect.CommitBLOBTran; + FBlobHandle := InvalidOid; + FBlobOpen := False; + end; +end; + +procedure TNativeDataSet.FreeBlob(PRecord: Pointer; FieldNo: Word); +var + AField : TPSQLField; + Buff : Pointer; +begin + AField := Fields[FieldNo]; + CheckParam(AField.FieldType <> fldBLOB, DBIERR_NOTABLOB); + AField.Buffer := PRecord; + Buff := AField.FieldValue; + if PAnsiDACChar(Buff)^ = #1 then //blob stream was created + begin + PAnsiDACChar(Buff)^ := #0; + Inc(PAnsiDACChar(Buff)); + FreeAndNil(TBlobItem(Buff^).Blob); + end; + CloseBlob(FieldNo); +end; + +procedure TNativeDataSet.GetBlobSize(PRecord : Pointer; FieldNo : Word; var iSize : integer); +Var + AField : TPSQLField; + + function BlobSize(columnNumber: Integer; buff :Pointer): LongInt; + begin + Result := 0; + if AField.FieldSubType = fldstMemo then + begin + if Assigned(FieldBuffer(ColumnNumber - 1)) then + if FConnect.IsUnicodeUsed then + Result := Length(FConnect.RawToString(FieldBuffer(ColumnNumber-1))) * SizeOf(Char) + else + Result := FieldSize(ColumnNumber-1); + end + else + if FBlobOpen then + begin + Result := lo_lseek(FConnect.Handle, FLocalBHandle, 0, PG_SEEK_END); + lo_lseek(FConnect.Handle, FLocalBHandle, 0, PG_SEEK_SET); + end; + end; + + function ByteaSize(ColumnNumber: Integer):integer; + var P: PAnsiDACChar; + i, Len: integer; + begin + Result := 0; + if FieldBuffer(ColumnNumber-1) = nil then Exit; + P := FieldBuffer(ColumnNumber-1); + {$IFNDEF NEXTGEN} + Len := {$IFDEF DELPHI_18}{$IFNDEF NEXTGEN}System.AnsiStrings.{$ENDIF}{$ENDIF}StrLen(P); + {$ELSE} + Len := Length(MarshaledAString(P)); + {$ENDIF} + Result := 0; + case FConnect.NativeByteaFormat of + nbfEscape: + begin + I := 0; + while i <= Len - 1 do + begin + if P[i] = '\' then + begin + inc(i); + if P[i] = '\' then + inc(i) + else + inc(i,3); + end + else + inc(i); + inc(Result); + end; + end; + nbfHex: //for >9.0 + begin + Result := (Len - 2) div 2; // '\x' preceding bytea value + 2 hexadecimal digits per byte + end; + end; + end; + +var + Buff : Pointer; + +begin + AField := Fields[FieldNo]; + CheckParam(AField.FieldType <> fldBLOB,DBIERR_NOTABLOB); + AField.Buffer := PRecord; + if not AField.FieldNULL then + begin + Buff := AField.FieldValue; + if PAnsiDACChar(Buff)^ = #1 then + begin + Inc(PAnsiDACChar(Buff)); + iSize := TBlobItem(Buff^).Blob.Size; + end + else + if (AField.NativeBLOBType = nbtOID) or (AField.NativeType = FIELD_TYPE_TEXT) then + iSize := BlobSize(FieldNo, AField.FieldValue) + else + iSize := ByteaSize(FieldNo); + end //not FieldNULL + else + iSize := 0 +end; + +procedure TNativeDataSet.GetBlob(PRecord : Pointer; FieldNo : Word; iOffSet : Longint; iLen : Longint; pDest : Pointer; var iRead : integer); +var + AField : TPSQLField; + + function CachedBlobGet(Offset, Length: longint; buff, Dest: pointer): longint; + begin + if PAnsiDACChar(buff)^ = #1 then + begin + Inc(PAnsiDACChar(buff)); + with TBlobItem(buff^) do + begin + Blob.Seek(Offset, 0); + Result := Blob.Read(Dest^, Length) + end; + end + else + Result := 0; + end; + + function BlobGet(columnNumber: integer; Offset, ALength: LongInt; + Buff, Dest: Pointer): LongInt; + var + L, N: integer; + Len: LongInt; + S: string; + begin + Result := CachedBlobGet(Offset, ALength, Buff, Dest); + if Result = 0 then + if AField.FieldSubType = fldstMemo then + begin + if FConnect.IsUnicodeUsed then + begin + S := FConnect.RawToString(FieldBuffer(columnNumber - 1)); + Len := Length(S) * SizeOf(Char); + {$IFDEF DELPHI_12} + Move((PByte(S) + Offset)^, Dest^, ALength); + {$ELSE} + Move(Pointer(Integer(PByte(S)) + Offset)^, Dest^, ALength); + {$ENDIF} + end + else + begin + {$IFDEF DELPHI_12} + Move((PByte(FieldBuffer(columnNumber - 1)) + Offset)^, Dest^, ALength); + {$ELSE} + Move(Pointer(Integer(PByte(FieldBuffer(columnNumber - 1))) + Offset)^, Dest^, ALength); + {$ENDIF} + Len := FieldSize(columnNumber - 1); + end; + if (Offset + ALength >= Len) then + Result := Len - Offset + else + Result := ALength; + end + else + begin + if FBlobOpen then + begin + lo_lseek(FConnect.Handle, FlocalBHandle, Offset, PG_SEEK_SET); + L := 0; + Len := ALength; + if ALength > MAX_BLOB_SIZE then + begin + repeat + if Len > MAX_BLOB_SIZE then + N := lo_read(FConnect.Handle, FlocalBHandle, + PAnsiDACChar(Dest) + L, MAX_BLOB_SIZE) + else + N := lo_read(FConnect.Handle, FlocalBHandle, + PAnsiDACChar(Dest) + L, Len); + Dec(Len, MAX_BLOB_SIZE); + Inc(L, N); + until N < MAX_BLOB_SIZE; + Result := L; + end + else + Result := lo_read(FConnect.Handle, FlocalBHandle, + PAnsiDACChar(Dest), ALength); + end; + end; + end; + + function ByteaBlobGet(ColumnNumber: Integer; Offset, Length : LongInt; buff, Dest :Pointer) : LongInt; + var P: PAnsiDACChar; + Len: integer; + begin + Result := CachedBlobGet(Offset, Length, Buff, Dest); + if (Result = 0) and + Assigned(PAnsiDACChar(FieldBuffer(columnNumber - 1) + Offset)) then + begin + P := PQUnescapeBytea(FieldBuffer(columnNumber - 1), Len); + try + Move((P + Offset)^, Dest^, Length); + Result := Length; + finally + PQFreeMem(P); + end; + end; + end; + +begin + iRead := 0; + if Assigned(pDest) and (iLen > 0) then + begin + AField := Fields[FieldNo]; + CheckParam(AField.FieldType <> fldBLOB, DBIERR_NOTABLOB); + AField.Buffer := PRecord; + if not AField.FieldNull then + if (AField.NativeBLOBType = nbtOID) or (AField.NativeType = FIELD_TYPE_TEXT) then + iRead := BlobGet(FieldNo, iOffset, iLen, PAnsiDACChar(AField.Data) + AField.FieldNumber - 1 , pDest) + else + iRead := ByteaBLOBGet(FieldNo, iOffset, iLen, PAnsiDACChar(AField.Data) + AField.FieldNumber - 1 ,pDest) + end; +end; + +procedure TNativeDataSet.PutBlob(PRecord: Pointer; FieldNo: Word; iOffSet: Longint; iLen: Longint; pSrc : Pointer); +var + AField : TPSQLField; + + procedure BlobPut(ColumnNumber: Integer; Offset, Length : LongInt; pSrc, buff :Pointer); + begin + if PAnsiDACChar(buff)^ = #0 then + begin + PAnsiDACChar(buff)^ := #1; + Inc(PAnsiDACChar(buff)); + TBlobItem(buff^).Blob := TMemoryStream.Create; + end + else + Inc(PAnsiDACChar(buff)); + with TBlobItem(buff^) do + begin + Blob.Seek(Offset, 0); + if Length > 0 then + Blob.Write(pSrc^, Length) + else + if Offset = 0 then Blob.Clear; + end; + end; + +begin + AField := Fields[FieldNo]; + CheckParam(AField.FieldType <> fldBLOB,DBIERR_NOTABLOB); + AField.Buffer := PRecord; + BlobPut(FieldNo, iOffset, iLen, pSrc, PAnsiDACChar(AField.Data) + AField.FieldNumber-1); + AField.FieldChanged := True; + AField.FieldNull := (iOffset + iLen = 0); +end; + +procedure TNativeDataSet.TruncateBlob(PRecord : Pointer; FieldNo : Word; iLen : Longint); +begin + PutBlob(PRecord, FieldNo, 0, iLen, nil); +end; + +procedure TNativeDataSet.QuerySetParams(Params : TParams; SQLText : String); +var + Token, Temp, Value: string; + Param: TParam; + i: integer; + byName: boolean; + MS: {$IFDEF DELPHI_17}TBytesStream{$ELSE}TMemoryStream{$ENDIF}; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} + + function GetDateTime: string; + var ts: string; + begin + case Param.DataType of + ftDate: ts := 'mm-dd-yyyy'; + ftDateTime: ts := 'mm-dd-yyyy hh:nn:ss.zzz'; + ftTime: ts := 'hh:nn:ss'; + end; + if VarType(Param.Value) = VarDate then + Result := AnsiQuotedStr(FormatDateTime(ts, Param.Value, PSQL_FS),'''') + else + Result := AnsiQuotedStr(VarAsType(Param.Value, varString),''''); + end; + +begin + if Params.Count = 0 then Exit; + Temp := ''; + i := 0; + while SQLText <> '' do + begin + if (Temp <> '') and + {$IFNDEF NEXTGEN} + CharInSet(SQLText[1], [' ',#9]) then Temp := Temp + ' '; + {$ELSE} + SQLText[START_STR_INDEX].IsInArray([' ',#9]) then Temp := Temp + ' '; + {$ENDIF} + GetToken(SQLText, Token); + //Added: handle of ? params + if (Token = ':') or (Token = '?') then + begin + ByName := False; + if Token = ':' then + begin + GetToken(SQLText, Token); + if (length(Token) = 1) and + {$IFNDEF NEXTGEN} + CharInSet(Token[1], [':','=']) then //handling of double colon & assignment + {$ELSE} + Token[START_STR_INDEX].IsInArray([':','=']) then //handling of double colon & assignment + {$ENDIF} + begin + Temp := Temp + Token; + Continue; + end; + ByName := True; + end; + if (Token <> '') and (Token[START_STR_INDEX] = '[') then + begin + if Token[Length(Token)] = ']' then + Token := Copy(Token, 2, Length(Token)-2) + else + Token := Copy(Token, 2, Length(Token)-1); + end else + if (Token <> '') and + {$IFNDEF NEXTGEN} + CharInSet(Token[1], ['"','''']) then + {$ELSE} + Token[START_STR_INDEX].IsInArray(['"','''']) then + {$ENDIF} + begin + if Token[START_STR_INDEX] = Token[Length(Token)] then + Token := Copy(Token, 2, Length(Token)-2) + else + Token := Copy(Token, 2, Length(Token)-1); + end; + // if Params is set with ":" then select param by name + Param := nil; + if ByName then + Param := Params.FindParam(Token) + else + begin + if i < Params.Count then Param := Params[i]; + Inc(i); + end; + if not Assigned(Param) or (VarType(Param.Value) = varEmpty) or (VarType(Param.Value) = varNull) then + Value := 'NULL' + else + case Param.DataType of + ftADT: Value := 'DEFAULT'; + ftBLOB: begin + MS := {$IFDEF DELPHI_17}TBytesStream{$ELSE}TMemoryStream{$ENDIF}.Create; + try + MS.SetSize(Longint(Param.GetDataSize)); + if MS.Size > 0 then + Param.GetData(MS.{$IFDEF DELPHI_17}Bytes{$ELSE}Memory{$ENDIF}); + Value := BlobValue(MS, TPSQLParam(Param).DataTypeOID <> FIELD_TYPE_OID, True); + finally + MS.Free; + end; + end; + ftDate, ftTime, ftDateTime: Value := GetDateTime; + {$IFDEF DELPHI_12} + ftFMTBcd: if VarIsFMTBcd(Param.Value) then + Value := BcdToStr(VarToBcd(Param.Value), PSQL_FS) + else + Value := AnsiQuotedStr(VarAsType(Param.Value, varString),''''); + {$ENDIF} + else + case VarType(Param.Value) of + {$IFNDEF DELPHI_5} + varInt64, + {$ENDIF} + varSmallint, + varInteger, + varByte : Value := IntToStr(Param.Value); + varSingle, + varDouble, + varCurrency : Value := SQLFloatToStr(VarAsType(Param.Value, varDouble)); + varBoolean : if Param.Value then Value := 'TRUE' else Value := 'FALSE'; + else + {$IFDEF DELPHI_12} + if FConnect.IsUnicodeUsed then + Value := StrValue(PWideChar(Param.AsString)) + else + {$ENDIF} + {$IFNDEF NEXTGEN} + Value := StrValue(PAnsiDACChar(AnsiString(Param.AsString))); + {$ELSE} + Value := StrValue(M.AsAnsi(Param.AsString).ToPointer); + {$ENDIF} + end; + end; + Temp := Temp + Value; + end else + Temp := Temp + Token; + end; + SQLQuery := Trim(Temp); +end; + +procedure TNativeDataSet.RelRecordLock(bAll: Boolean); +begin + FIsLocked := FALSE; +end; + +procedure TNativeDataSet.ExtractKey(PRecord: Pointer;pKeyBuf: Pointer); +var + i : Word; + MKey : PAnsiDACChar; + AField : TPSQLField; + bBlank : Boolean; + Buffer : array [0..MAX_CHAR_LEN] of Char; + iFields : Word; +begin + if not Assigned(PRecord) then PRecord := CurrentBuffer; + {$IFNDEF NEXTGEN} + ZeroMemory(pKeyBuf, FKeyDesc.iKeyLen); + {$ELSE} + FillChar(pKeyBuf^, FKeyDesc.iKeyLen, 0); + {$ENDIF} + MKey := pKeyBuf; + iFields := FKeyDesc.iFldsinKey; + for i := 0 to iFields-1 do + begin + AField := Fields[FKeyDesc.aiKeyFld[i]]; + NativeToDelphi(AField, PRecord, @Buffer, bBlank); + if not bBlank then + AdjustDelphiField(AField, @Buffer, MKey); + if bBlank then + {$IFNDEF NEXTGEN} + ZeroMemory(MKey, AField.NativeSize); + {$ELSE} + FillChar(MKey^, AField.NativeSize, 0); + {$ENDIF} + Inc(MKey, AField.NativeSize + 1); + end; +end; + + +procedure TNativeDataSet.GetIndexDesc(iIndexSeqNo: Word; var idxDesc: IDXDesc); +begin + CheckParam(not(IndexCount > 0) ,DBIERR_NOASSOCINDEX); + Finalize(idxDesc); + {$IFNDEF NEXTGEN} + ZeroMemory(@idxDesc, Sizeof(idxDesc)); + {$ELSE} + FillChar(idxDesc, Sizeof(idxDesc), 0); + {$ENDIF} + if (iIndexSeqNo = 0) and not FGetKeyDesc then + if KeyNumber <> 0 then iIndexSeqNo := KeyNumber; + if iIndexSeqNo = 0 then iIndexSeqNo := 1; + CheckParam(FIndexDescs.mIndex[iIndexSeqNo] = nil,DBIERR_NOSUCHINDEX); + idxDesc := FIndexDescs.mIndex[iIndexSeqNo].Description; +end; + +procedure TNativeDataSet.GetIndexDescs(Descs: TIDXDescList); +var + Props : CURProps; + i : Word; +begin + GetCursorProps(Props); + if Props.iIndexes > 0 then + begin + FGetKeyDesc := TRUE; + try + for i := 1 to Props.iIndexes do + GetIndexDesc(i, Descs[i-1]); + finally + FGetKeyDesc := FALSE; + end; + end; +end; + +procedure TNativeDataSet.SwitchToIndex( pszIndexName, pszTagName : string; iIndexId : Word; bCurrRec : Boolean); + +procedure ParseIndexName(pszIndexName: string; Var iIndexId : Word; pszTrueName : string); +var + //S : ShortString; + Found : Boolean; + Desc : IDXDesc; + s: String; +begin + Found := False; + FGetKeyDesc := TRUE; + try + iIndexId := 1; + Repeat + GetIndexDesc ( iIndexId, Desc ); + s:= Desc.szName; + if Desc.szName = pszIndexName then + begin + Found := TRUE; + break; + end; + Inc(iIndexId); + Until Found; + if Found and ( iIndexId > 0 ) and ( pszTrueName <> '' ) then + pszTrueName := Desc.szName; + finally + FGetKeyDesc := False; + end; +end; + +begin + FIsLocked := FALSE; + //CheckParam(pszIndexName = '', DBIERR_INVALIDPARAM); + if FFieldDescs.Count = 0 then InitFieldDescs; + if Length(pszIndexName) > 0 then + ParseIndexName(pszIndexName, iIndexId, '') + else + if FPrimaryKeyNumber >= 1 then iIndexId := FPrimaryKeyNumber; + try + if Ranges then ResetRange; + KeyNumber := iIndexId; + finally + AutoReExec := True; + end; + GetIndexDesc(iIndexId, FKeyDesc); +end; + +procedure TNativeDataSet.ResetRange; +begin + RangeClause.Clear; + if Ranges then ReOpenTable; + Ranges := False; +end; + +procedure TNativeDataSet.SetRange(bKeyItself: Boolean; + iFields1: Word;iLen1: Word;pKey1: Pointer;bKey1Incl: Boolean; + iFields2: Word;iLen2: Word;pKey2: Pointer;bKey2Incl: Boolean); + + procedure CreateRangeClause(First : Boolean; bKeyItself: Boolean;iFields: Word;iLen: Word; pKey: Pointer; bKeyIncl: Boolean); + var + i : integer; + AField : TPSQLField; + WHERE : String; + FldVal : String; + bBlank : Boolean; + Buff : array[0..MAX_CHAR_LEN] of AnsiDACByteChar; + CurBuffer : PAnsiDACChar; + TimeStamp: TTimeStamp; + begin + For i := 0 to iFields-1 do + if Fields[FKeyDesc.aiKeyFld[i]].FieldNull then + begin + RangeClause.Text := 'WHERE 1=0'; + Exit; //null values have no details by standard + end; + + WHERE := ''; + CurBuffer := PAnsiDACChar(pKey); + for i := 0 to iFields-1 do + begin + AField := Fields[FKeyDesc.aiKeyFld[i]]; + if bKeyItself then + AdjustNativeField(AField, CurBuffer, @Buff, bBlank) + else + NativeToDelphi(AField, CurBuffer, @Buff, bBlank); + Inc(CurBuffer, AField.NativeSize + 1); + if bBlank then Continue; //19.05.2008 + if RangeClause.Count > 0 then WHERE := 'and ' else WHERE := 'where '; + WHERE := WHERE + AnsiQuotedStr(AField.FieldName,'"'); + if First then WHERE := WHERE + '>' else WHERE := WHERE + '<'; + if bKeyIncl then WHERE := WHERE + '='; + case AField.Fieldtype of + fldINT16: FldVal := IntToStr(PSmallInt(@Buff)^); + fldINT32: FldVal := IntToStr(PLongInt(@Buff)^); + fldFLOAT: FldVal := SQLFloatToStr(PDouble(@Buff)^); + fldBOOL: if PBoolean(@Buff)^ {$IFDEF FPC} <> 0{$ENDIF} then FldVal := 'True' else FldVal := 'False'; + fldZSTRING: FldVal := StrValue(@Buff); + fldUUID: FldVal := UuidValue(@Buff); + fldINT64: FldVal := IntToStr(PInt64(@Buff)^); + fldDate: + begin + TimeStamp.Date := PLongInt(@Buff)^; + TimeStamp.Time := 0; + FldVal := '''' + DateTimeToSqlDate(TimeStampToDateTime(TimeStamp),1) + ''''; + end; + fldTime: begin + TimeStamp.Date := DateDelta; + TimeStamp.Time := PLongInt(@Buff)^; + FldVal := '''' + DateTimeToSqlDate(TimeStampToDateTime(TimeStamp),2) + ''''; + end; + fldTIMESTAMP: FldVal := '''' + DateTimeToSqlDate(TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF}(PDouble(@Buff)^))),0) + ''''; + end; + WHERE := WHERE + Trim(FldVal); + RangeClause.Add(WHERE); + end; + end; + +begin + try + RangeClause.Clear; + Ranges := True; + CreateRangeClause(True,bKeyItself, iFields1, iLen1, pKey1, bKey1Incl); + CreateRangeClause(False,bKeyItself, iFields2, iLen2, pKey2, bKey2Incl); + ReOpenTable; + except + ResetRange; + end; +end; + +procedure TNativeDataSet.SetKeyNumber( newValue : SmallInt ); +var + x,y : Integer; + Ind : TPSQLIndex; + +function GetOrderByStr(Idx : TPSQLIndex; index : integer) : String; +var + B : Boolean; +begin + result := ''; + B := idx.Descending; + Result := AnsiQuotedStr(string(FieldInfo[idx.FDesc.aiKeyFld[index]-1].FieldName), '"'); + if B then + Result := Result +' DESC'; +end; + + +begin + if newValue <> FKeyNumber then + begin + OrderClause.Clear; + if newValue <= IndexCount then + begin + OrderClause.Add('ORDER BY '); + Ind := FIndexDescs.mIndex[newValue]; + y := ind.FDesc.iFldsInKey-1; + for x := 0 to y-1 do + OrderClause.Add(GetOrderByStr(Ind, x) + ','); + OrderClause.Add(GetOrderByStr(Ind, y)); + end; + FKeyNumber := newValue; + ReOpenTable; + end; +end; + +procedure TNativeDataSet.ReOpenTable; +var OldRowsAffected: integer; +begin + OldRowsAffected := FAffectedRows; + OpenTable; + FAffectedRows := OldRowsAffected; +end; + +procedure TNativeDataSet.ClearIndexInfo; +begin + if FIndexDescs.Count > 0 then + begin + FIndexDescs.Clear; + FIndexDescs.Updated := False; + end; + FKeyNumber := 0; + FPrimaryKeyNumber := 0; +end; + +procedure TNativeDataSet.SettoSeqNo(iSeqNo: Longint); +begin + if iSeqNo - 1 < 0 then + RecNo := -1 + else + if iSeqNo - 1 >= RecordCount - 1 then + RecNo := RecordCount - 1 + else + RecNo := iSeqNo - 1; + CurrentRecord(RecNo); +end; + + +procedure TNativeDataSet.EmptyTable; +var + S : String; + Result : PPGResult; +begin + S := Format('TRUNCATE TABLE %s',[TableName]); + FAffectedRows := 0; + if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; + Result := _PQexecute(FConnect, S); + if Result <> nil then + begin + FConnect.CheckResult; + PQClear(Result); + end else + FConnect.CheckResult; +end; + +procedure TNativeDataSet.AddIndex(var IdxDesc: IDXDesc; pszKeyviolName : string); +var + Result : PPGResult; + + function CreateSQLForAddIndex: String; + var + Fld : String; + PSQLIdxs : TPSQLIndexes; + begin + Result := ''; + PSQLIdxs := TPSQLIndexes.Create(nil); + TPSQLIndex.CreateIndex(PSQLIdxs,@IdxDesc); + Fld := SQLCreateIdxStr(PSQLIdxs[1],TableName,Fields); + Result := Result+Fld; + PSQLIdxs.Free; + end; + +begin + if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; + Result := _PQexecute(FConnect, CreateSQLForAddIndex); + if Result <> nil then + begin + PQclear(Result); + end else + FConnect.CheckResult; +end; + +procedure TNativeDataSet.DeleteIndex(pszIndexName: string; pszIndexTagName: string; iIndexId: Word); +var + Result : PPGResult; +begin + if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; + Result := _PQexecute(FConnect, Format('DROP INDEX %s ON %s',[pszIndexName, TableName])); + if Result <> nil then + begin + PQclear(Result); + end else + FConnect.CheckResult; +end; + +procedure TNativeDataSet.AcqTableLock(eLockType: word; bNoWait: boolean); +const _lockmode: array[TPSQLLockType] of DACAString = ('ACCESS SHARE', 'ROW SHARE', + 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', + 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', + 'ACCESS EXCLUSIVE'); + _nowait: array[boolean] of DACAString = ('', 'NOWAIT'); +var Res: PPGresult; +begin + Res := _PQExecute(FConnect, Format('LOCK TABLE %s IN %s MODE %s', [TableName, _lockmode[TPSQLLockType(eLockType)], _nowait[bNoWait]])); + try + FConnect.CheckResult(Res); + finally + PQclear(RES); +end; +end; + +procedure TNativeDataSet.SetToKey(eSearchCond: DBISearchCond; bDirectKey: Boolean;iFields: Word;iLen: Word;pBuff: Pointer); +var + FldNo : Integer; + AField : TPSQLField; + Item : TPSQLIndex; + R : LongInt; + I : Integer; + Flds : array of integer; + SFlds : array of String; + +begin + Item := FIndexDescs.mIndex[iFields]; + SetLength(Flds,Item.Description.iFldsInKey); + SetLength(SFlds,Item.Description.iFldsInKey); + for I :=0 to Item.Description.iFldsInKey-1 do + begin + FldNo := Item.Description.aiKeyFld[I]; + AField := Fields[FldNo]; + Flds[i] := FldNo-1; + SFlds[I] := FieldVal(AField.FieldNumber,AField.FieldValue); + end; + R := findrows(Flds,SFlds,False,ilen); + if (R <> -1) then + SetToSeqNo(R+1) else + SetToSeqNo(RecordCount); +end; + + +procedure TNativeDataSet.Clone(bReadOnly : Boolean; bUniDirectional : Boolean; var hCurNew : hDBICur); +begin + if FConnect = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); + TNativeConnect(FConnect).OpenTable(TableName, FIndexName, 0, FOMode, dbiOPENSHARED, hCurNew, [], 0, 0); + TNativeDataSet(hCurNew).MasterCursor := Self; +end; + +procedure TNativeDataSet.SetToCursor(hDest : hDBICur); +var + M : Pointer; +begin + if hDest = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); + M := AllocMem(BookMarkSize); + try + if MasterCursor = nil then + begin + GetBookMark(M); + TNativeDataSet(hDest).SetToBookMark(M); // set main cursor bookmark + end; + finally + FreeMem(M, BookMarkSize); + end; +end; + + + +////////////////////////////////////////////////////////////////////// +// TIndexList Object // +////////////////////////////////////////////////////////////////////// +constructor TIndexList.Create(PSQL: TNativeConnect; D : TIDXDescList; TotalCount : integer ); +begin + inherited Create(PSQL, [], '', '', 0, 0, 0); + Items := TotalCount; + if D <> nil then + begin + SetLength(Descs, Items); + Descs := Copy(D, Low(D), Length(D)); + end; + SetToBegin; +end; + +procedure TIndexList.SetToBegin; +begin + inherited SetToBegin; + Position := 0; +end; + +Destructor TIndexList.Destroy; +begin + Finalize(Descs); + Inherited Destroy; +end; + +procedure TIndexList.GetNextRecord(eLock: DBILockType; PRecord : Pointer;pRecProps : pRECProps); +begin + if Position = Items then + raise EPSQLException.CreateBDE(DBIERR_EOF) + else + GetIdxDesc(PRecord); + Inc(Position); +end; + +procedure TIndexList.GetIdxDesc(Precord: PIdxDesc); +begin + PRecord^ := TIDXDescList(Descs)[Position]; +end; + + +function TIndexList.GetBufferSize : integer; +begin + Result := SizeOf(idxDESC); +end; + +function TIndexList.GetWorkBufferSize : integer; +begin + Result := GetBufferSize; +end; + +procedure TIndexList.SetToBookmark(P : Pointer); +begin + SetToBegin; +end; + +procedure TIndexList.GetRecordCount( Var iRecCount : integer ); +begin + iRecCount := Items; +end; + +constructor TFieldList.Create(PSQL: TNativeConnect; D: TFLDDescList; + TotalCount: integer); +begin + inherited Create(PSQL, [], '', '', 0, 0, 0); + Items := TotalCount; + if D <> nil then + begin + SetLength(Descs, Items); + Descs := Copy(D, Low(D), Length(D)); + end; + SetToBegin; + +end; + +destructor TFieldList.Destroy; +begin + Finalize(Descs); + inherited Destroy; +end; + +function TFieldList.GetBufferSize : integer; +begin + Result := SizeOf(FLDDesc); +end; + +{******************************************************************************} +{ TPSQLEngine } +{******************************************************************************} +constructor TPSQLEngine.Create(P : TObject; Container : TContainer); +begin + Inherited Create(P, Container); + FDatabase := DAChDBIDb(Self); +end; + +Destructor TPSQLEngine.Destroy; +begin + FDatabase := nil; + Inherited Destroy; +end; + +function TPSQLEngine.GetDatabase : DAChDBIDb; +begin + Result := FDatabase; +end; + +procedure TPSQLEngine.SetDatabase( H : DAChDBIDb ); +begin + if H = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); + FDatabase := H; +end; + +function TPSQLEngine.IsSqlBased(hDb : DAChDBIDb) : Boolean; +begin + Result := True; +end; + +function TPSQLEngine.OpenDatabase(Params : TStrings; UseSinleLineConnInfo: boolean; var hDb : DAChDBIDb): DBIResult; +Var + DB : TNativeConnect; +begin + FDatabase := nil; + try + Db := TNativeConnect.Create; + if Db = nil then + raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); + try + if UseSinleLineConnInfo then + begin + DB.ProcessDBParams(Params); + Db.InternalConnect; + end + else + DB.InternalConnect(Params); + except + FreeAndNil(DB); + raise; + end; + hDb := DAChDBIDb(DB); + Database := hDb; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.CloseDatabase(var hDb : DAChDBIDb) : DBIResult; +begin + try + Database := hDb; + {$IFNDEF NEXTGEN} + TNativeConnect(hDb).Free; + {$ELSE} + TNativeConnect(hDb).DisposeOf; + {$ENDIF} + hDb := nil; + FDatabase := nil; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenTable(hDb: DAChDBIDb; pszTableName: string; pszIndexName: string; iIndexId: Word; + eOpenMode: DBIOpenMode;eShareMode: DBIShareMode; var hCursor: hDBICur; + AnOptions: TPSQLDatasetOptions; Limit, Offset : Integer): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).OpenTable(pszTableName, pszIndexName, iIndexId, eOpenMode, eShareMode, hCursor, AnOptions, Limit, Offset); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenStoredProcList(hDb: DAChDBIDb;pszWild: string; List : TStrings): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).StoredProcList(pszWild, List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenTableList(hDb: DAChDBIDb;pszWild: string; SystemTables: Boolean; List : TStrings): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).TableList(pszWild, SystemTables, List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenSchemaList(hDb: DAChDBIDb; pszWild: string; SystemSchemas: Boolean; List : TStrings): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).SchemaList(pszWild, SystemSchemas, List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenUserList(hDb: DAChDBIDb; pszWild: string; List : TStrings): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).UserList(pszWild, List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetNextRecord(hCursor: hDBICur;eLock: DBILockType;pRecBuff : Pointer;pRecProps: pRECProps): DBIResult; +begin + try + TNativeDataSet(hCursor).GetNextRecord(eLock, pRecBuff, pRecProps); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetToBookMark(hCur: hDBICur;pBookMark: Pointer) : DBIResult; +begin + try + TNativeDataSet(hCur).SetToBookMark(pBookMark); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.CompareBookMarks(hCur : hDBICur;pBookMark1,pBookMark2 : Pointer;Var CmpBkmkResult : CmpBkmkRslt): DBIResult; +begin + try + TNativeDataSet(hCur).CompareBookMarks(pBookMark1, pBookMark2, CmpBkmkResult); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetRecord (hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; +begin + try + TNativeDataSet(hCursor).GetRecord(eLock,PRecord,pRecProps); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetPriorRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer;pRecProps: pRECProps): DBIResult; +begin + try + TNativeDataSet(hCursor).GetPriorRecord(eLock, PRecord, pRecProps); + Result := DBIERR_NONE; + except + Result := CheckError; + if Result = DBIERR_EOF then Result := DBIERR_BOF; + end; +end; + +function TPSQLEngine.GetBookMark(hCur: hDBICur;pBookMark : Pointer) : DBIResult; +begin + try + TNativeDataSet(hCur).GetBookMark(pBookMark); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ReadBlock(hCursor : hDBICur; var iRecords : integer; pBuf : Pointer): DBIResult; +begin + try + TNativeDataset(hCursor).ReadBlock(iRecords, pBuf); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetRecordCount(hCursor : hDBICur;Var iRecCount : integer) : DBIResult; +begin + try + TNativeDataSet(hCursor).GetRecordCount(iRecCount); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ForceReread(hCursor: hDBICur): DBIResult; +begin + try + TNativeDataSet(hCursor).ForceReread; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pDest: Pointer;var bBlank: Boolean): DBIResult; +begin + try + TNativeDataSet(hCursor).GetField(FieldNo, PRecord, PDest, bBlank); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.CloseCursor(hCursor : hDBICur) : DBIResult; +begin + try + TNativeDataSet(hCursor).CloseTable; + {$IFDEF NEXTGEN} + TNativeDataSet(hCursor).DisposeOf; + {$ELSE} + TNativeDataSet(hCursor).Free; + {$ENDIF} + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.PutField(hCursor: hDBICur;FieldNo: Word;PRecord: Pointer;pSrc: Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).PutField(FieldNo,PRecord,PSrc); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;eOpenMode: DBIOpenMode): DBIResult; +begin + try + TNativeDataSet(hCursor).OpenBlob(PRecord, FieldNo, eOpenMode); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetBlobSize(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;var iSize: integer): DBIResult; +begin + try + TNativeDataSet(hCursor).GetBlobSize(PRecord, FieldNo, iSize); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint;pDest: Pointer;var iRead: integer): DBIResult; +begin + try + TNativeDataSet(hCursor).GetBlob(PRecord, FieldNo, iOffset, iLen, pDest, iRead); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.Ping(Params: TStrings; + var PingResult: TPingStatus): DBIResult; +begin + try + PingResult := TNativeConnect.Ping(Params); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.PutBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iOffSet: Longint;iLen: Longint;pSrc: Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).PutBlob(PRecord, FieldNo, iOffset, iLen, pSrc); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.TruncateBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word;iLen: Longint): DBIResult; +begin + try + TNativeDataSet(hCursor).TruncateBlob( PRecord, FieldNo, iLen ); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.FreeBlob(hCursor: hDBICur;PRecord: Pointer;FieldNo: Word): DBIResult; +begin + try + TNativeDataSet(hCursor).FreeBlob(PRecord, FieldNo); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.CloseBlob(hCursor: hDBICur; FieldNo: Word): DBIResult; +begin + try + TNativeDataSet(hCursor).CloseBlob(FieldNo); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.BeginTran(hDb: DAChDBIDb; eXIL: eXILType; var hXact: hDBIXact): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).BeginTran(eXIL, hXact); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.EndTran(hDb: DAChDBIDb;hXact : hDBIXact; eEnd : eXEnd): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).EndTran(hXact,eEnd); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetTranInfo(hDb : DAChDBIDb; hXact : hDBIXact; pxInfo : pXInfo): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).GetTranInfo(hXact,pxInfo); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + + +function TPSQLEngine.GetTranStatus(hDb: DAChDBIDb; var TranStatus: TTransactionStatusType): DBIResult; +begin + try + Database := hDb; + TranStatus := TNativeConnect(hDb).GetTransactionStatus; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Pointer;iMaxLen: integer;var iLen: integer): DBIResult; +begin + iLen := 0; + if Assigned(hObj) then + begin + TNativeDataSet(hObj).GetProp( iProp, PropValue, iMaxLen, iLen ); + Result := DBIERR_NONE; + end else + Result := DBIERR_INVALIDPARAM; +end; + +function TPSQLEngine.SetEngProp(hObj: hDBIObj;iProp: Longint;PropValue: Longint): DBIResult; +begin + try + if Assigned(hObj) then + begin + TNativeDataSet(hObj).SetProp(iProp, PropValue); + Result := DBIERR_NONE; + end else + Result := DBIERR_INVALIDPARAM; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetVchkDesc(hCursor: hDBICur;iValSeqNo: Word; var pvalDesc: VCHKDesc): DBIResult; +begin + try + TNativeDataSet(hCursor).GetVchkDesc(iValSeqNo, pvalDesc); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetCursorProps(hCursor: hDBICur;var curProps: CURProps): DBIResult; +begin + try + TNativeDataSet(hCursor).GetCursorProps(curProps); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetFieldDescs(hCursor: hDBICur; var pfldDesc : TFLDDescList): DBIResult; +begin + try + TNativeDataSet(hCursor).GetFieldDescs(pFldDesc); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetToBegin(hCursor : hDBICur) : DBIResult; +begin + TNativeDataSet(hCursor).SetToBegin; + Result := DBIERR_NONE; +end; + +function TPSQLEngine.SetToEnd(hCursor : hDBICur) : DBIResult; +begin + TNativeDataSet(hCursor).SetToEnd; + Result := DBIERR_NONE; +end; + +function TPSQLEngine.RelRecordLock(hCursor: hDBICur;bAll: Boolean): DBIResult; +begin + try + TNativeDataSet(hCursor).RelRecordLock(bAll); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.InitRecord(hCursor: hDBICur;PRecord: Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).InitRecord(PRecord); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.InsertRecord(hCursor: hDBICur;eLock: DBILockType;PRecord: Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).InsertRecord(eLock, PRecord); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.AppendRecord(hCursor : hDBICur;PRecord : Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).AppendRecord(PRecord); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ModifyRecord(hCursor: hDBICur;OldRecord,PRecord: Pointer;bFreeLock : Boolean; ARecNo : LongInt): DBIResult; +begin + try + TNativeDataSet(hCursor).ModifyRecord(OldRecord,PRecord, bFreeLock,ARecNo); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.DeleteRecord(hCursor: hDBICur;PRecord: Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).DeleteRecord(PRecord); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetToSeqNo(hCursor: hDBICur;iSeqNo: Longint): DBIResult; +begin + try + TNativeDataSet(hCursor).SettoSeqNo(iSeqNo); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.AddFilter(hCursor: hDBICur;iClientData: Longint;iPriority: Word;bCanAbort: Boolean;pcanExpr: pCANExpr; + pfFilter: pfGENFilter;var hFilter: hDBIFilter): DBIResult; +begin + try + TNativeDataSet(hCursor).AddFilter(iClientData,iPriority, bCanAbort,pcanExpr, pfFilter, hFilter ); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.DropFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; +begin + try + TNativeDataSet(hCursor).DropFilter(hFilter); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ActivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; +begin + try + TNativeDataSet(hCursor).ActivateFilter(hFilter); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.DeactivateFilter(hCursor: hDBICur;hFilter: hDBIFilter): DBIResult; +begin + try + TNativeDataSet(hCursor).DeactivateFilter(hFilter); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetErrorString(rslt: DBIResult; var ErrorMsg: String): DBIResult; +begin + ErrorMsg := MessageStatus; + Result := rslt; +end; + +function TPSQLEngine.QExecDirect(hDb : DAChDBIDb; pszQuery: String;phCur : phDBICur; var AffectedRows : integer): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).QExecDirect(pszQuery,phCur, AffectedRows); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.QAlloc(hDb: DAChDBIDb; var hStmt: hDBIStmt): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).QueryAlloc(hStmt); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.QPrepare(hStmt: hDBIStmt;pszQuery: String): DBIResult; +begin + try + TNativeConnect(Database).QueryPrepare(hStmt,pszQuery); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.QExec(hStmt: hDBIStmt; phCur : phDBICur; var AffectedRows: integer): DBIResult; +begin + try + if phCur = nil then + begin + try + TNativeDataSet(hStmt).Execute; + AffectedRows := TNativeDataSet(hStmt).FAffectedRows; + Result := DBIERR_NONE; + except + Result := CheckError; + end + end + else + begin + TNativeDataSet(hStmt).OpenTable; + if TNativeDataSet(hStmt).FStatement <> nil then + phCur^ := hDBICur(hStmt) + else + phCur^ := nil; + Result := DBIERR_NONE; + end; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.QFree(var hStmt : hDBIStmt): DBIResult; +begin + Result := CloseCursor(hDBICur(hStmt)); + hStmt := nil; +end; + +function TPSQLEngine.QuerySetParams(hStmt: hDBIStmt;Params : TParams; SQLText : String): DBIResult; +begin + try + TNativeDataSet(hStmt).QuerySetParams(Params,SQLText); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +{$IFDEF DELPHI_5} +type + PRaiseFrame = ^TRaiseFrame; + TRaiseFrame = packed record + NextRaise: PRaiseFrame; + ExceptAddr: Pointer; + ExceptObject: TObject; + ExceptionRecord: PExceptionRecord; + end; + +function AcquireExceptionObject: Pointer; +begin + if RaiseList <> nil then + begin + Result := PRaiseFrame(RaiseList)^.ExceptObject; + PRaiseFrame(RaiseList)^.ExceptObject := nil; + end + else + Result := nil; +end; +{$ENDIF} + +function TPSQLEngine.CheckError : DBIResult; +var + ExceptionPtr : Pointer; +begin + Result := DBIERR_NONE; + + ExceptionPtr := AcquireExceptionObject; // ExceptObject; + + if not Assigned(ExceptionPtr) then Exit; + + if TObject(ExceptionPtr) is EPSQLException then + begin + if EPSQLException(ExceptionPtr).BDEErrors then + Result := EPSQLException(ExceptionPtr).BDEErrorCode + else + begin + FNativeStatus := EPSQLException(ExceptionPtr).PSQLErrorCode; + Result := 1001; + end; + FNativeMsg := EPSQLException(ExceptionPtr).PSQLErrorMsg; + TObject(ExceptionPtr).Free; + {$IFDEF DELPHI_7} + ReleaseExceptionObject; + {$ENDIF} + end + else + raise TObject(ExceptionPtr); +end; + +function TPSQLEngine.GetDatabases(hDb: DAChDBIDb; pszWild: string; List : TStrings):DBIResult; +begin + try + Database := hDb; + TNativeConnect(hdb).DatabaseList(pszWild,List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetCharacterSet(hDb : DAChDBIDb; var CharSet : string):DBIResult; +begin + try + Database := hDb; + CharSet := TNativeConnect(hDb).GetCharSet; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +/////////////////////////////////////////////////////////////////////////////// +// Reserver for TPSQLTable // +/////////////////////////////////////////////////////////////////////////////// +function TPSQLEngine.OpenFieldList(hDb: DAChDBIDb; pszTableName: string; pszDriverType: string; bPhyTypes: Boolean; var hCur: hDBICur): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).OpenFieldList(pszTableName, pszDriverType, bPhyTypes, hCur ); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.OpenIndexList(hDb: DAChDBIDb;pszTableName: string; pszDriverType: string; var hCur: hDBICur): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).OpenIndexList(pszTableName, pszDriverType, hCur); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.EmptyTable(hDb: DAChDBIDb; hCursor : hDBICur; pszTableName : string; pszDriverType : string): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).EmptyTable(hCursor, pszTableName); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetRange(hCursor: hDBICur;bKeyItself: Boolean;iFields1: Word;iLen1: Word;pKey1: Pointer;bKey1Incl: Boolean; + iFields2: Word;iLen2: Word;pKey2: Pointer;bKey2Incl: Boolean): DBIResult; +begin + try + TNativeDataSet(hCursor).SetRange(bKeyItself, iFields1, iLen1, pKey1, bKey1Incl,iFields2, iLen2, pKey2, bKey2Incl); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ResetRange(hCursor: hDBICur): DBIResult; +begin + try + TNativeDataSet(hCursor).ResetRange; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SwitchToIndex(hCursor: hDBICur; pszIndexName, pszTagName: string; iIndexId: Word; bCurrRec: Boolean): DBIResult; +begin + try + TNativeDataSet(hCursor).SwitchToIndex(pszIndexName, pszTagName, iIndexId, bCurrRec); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ExtractKey(hCursor: hDBICur;PRecord: Pointer;pKeyBuf: Pointer): DBIResult; +begin + try + TNativeDataSet(hCursor).ExtractKey(PRecord, pKeyBuf); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetRecordForKey(hCursor: hDBICur; bDirectKey: Boolean; iFields: integer; iLen: integer; pKey: Pointer; pRecBuff: Pointer; AStrictConformity: boolean = False): DBIResult; +begin + try + TNativeDataSet(hCursor).GetRecordForKey(bDirectKey,iFields,iLen, pKey, pRecBuff, AStrictConformity); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.AddIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;var IdxDesc: IDXDesc;pszKeyviolName : string): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDB).AddIndex(hCursor, pszTableName, pszDriverType, idxDesc, pszKeyViolName); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.DeleteIndex(hDb: DAChDBIDb;hCursor: hDBICur;pszTableName: string;pszDriverType: string;pszIndexName: string;pszIndexTagName: string;iIndexId: Word): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDB).DeleteIndex(hCursor, pszTableName, pszDriverType, pszIndexName, pszIndexTagName, iIndexId); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetIndexDesc(hCursor: hDBICur;iIndexSeqNo: Word;var idxDesc: IDXDesc): DBIResult; +begin + try + Result := DBIERR_NONE; + if TNativeDataSet(hCursor).isQuery then + Result := DBIERR_NOASSOCINDEX + else + TNativeDataSet(hCursor).GetIndexDesc(iIndexSeqNo,idxDesc); + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetIndexDescs(hCursor: hDBICur; idxDescs: TIDXDescList): DBIResult; +begin + try + TNativeDataSet(hCursor).GetIndexDescs(idxDescs); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.TranslateRecordStructure(pszSrcDriverType : PChar;iFlds: Word;pfldsSrc: pFLDDesc;pszDstDriverType: PChar; pszLangDriver: PChar;pfldsDst: pFLDDesc; bCreatable: Boolean): DBIResult; +var + M : pFldDesc; + I : Integer; +begin + try + M := pfldsDst; + For i := 1 to iFlds do + begin + Move(pfldsSrc^, M^, SizeOf(FldDesc)); + Inc(M); + Inc(pfldsSrc); + end; + Result :=DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.AcqTableLock(hCursor: hDBICur; eLockType: word; bNoWait: boolean): DBIResult; +begin + try + TNativeDataset(hCursor).AcqTableLock(eLockType, bNoWait); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetToKey(hCursor: hDBICur; + eSearchCond: DBISearchCond; + bDirectKey: Boolean; + iFields: integer; + iLen: integer; + pBuff: Pointer): DBIResult; +begin + try + TNativeDataset(hCursor).SetToKey(eSearchCond, bDirectKey, iFields, iLen, pBuff); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TNativeDataSet.SetRowPosition(iFields: integer; LID: int64; pRecBuffer: Pointer): Boolean; +var + FldNo: integer; + AField: TPSQLField; + Item: TPSQLIndex; + R: LongInt; + i: integer; + Flds: array of integer; + SFlds: array of String; + K: integer; + +var + SS: String; + +begin + if isQuery and (FOMode = dbiREADONLY) then + iFields := -1; // multitable or non-Select SQL query + + if iFields = -1 then + begin + K := 1; + for i := 0 to Fields.Count - 1 do + begin + AField := Fields[i + 1]; + AField.Buffer := pRecBuffer; + if (AField.FieldType = fldBLOB) or (AField.Description.bCalcField) or + (AField.FieldNull and (AField.FieldSubType <> fldstAUTOINC)) or (AField.NativeType = FIELD_TYPE_TIMESTAMP) then + Continue; + SetLength(Flds, K); + SetLength(SFlds, K); + Flds[K - 1] := i; + if (AField.FieldSubType = fldstAUTOINC) and (LID > 0) then + SFlds[K - 1] := inttostr(LID) + else + SFlds[K - 1] := FieldVal(i + 1, AField.FieldValue); + INC(K); + end; + end + else + begin + Item := FIndexDescs.mIndex[iFields]; + SetLength(Flds, Item.Description.iFldsInKey); + SetLength(SFlds, Item.Description.iFldsInKey); + for i := 0 to Item.Description.iFldsInKey - 1 do + begin + FldNo := Item.Description.aiKeyFld[i]; + AField := Fields[FldNo]; + Flds[i] := FldNo - 1; + AField.Buffer := pRecBuffer; + SS := FieldVal(FldNo, AField.FieldValue); + if SS = '' then + begin + if (AField.FieldSubType = fldstAUTOINC) and (LID > 0) then + SS := inttostr(LID); + end; + SFlds[i] := SS; + end; + end; + R := findrows(Flds, SFlds, False, 0); + Result := R <> -1; + if Result then + SettoSeqNo(R + 1); +end; + + +function TPSQLEngine.CloneCursor(hCurSrc: hDBICur;bReadOnly: Boolean;bUniDirectional: Boolean;var hCurNew: hDBICur): DBIResult; +begin + try + TNativeDataset(hCurSrc).Clone(bReadonly, bUniDirectional, hCurNew); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetToCursor(hDest, hSrc : hDBICur) : DBIResult; +begin + try + TNativeDataset(hSrc).SetToCursor(hDest); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +{PSQLNotify} +function TPSQLEngine.OpenPGNotify(hDb: DAChDBIDb; var hNotify: hDBIObj): DBIResult; +//Var +// ANotify : TNativePGNotify; +begin + try + hNotify := nil; + Database := hDB; + {ANotify} hNotify := hDBIObj(TNativePGNotify.Create(TNativeConnect(Database))); + if hNotify = nil then raise EPSQLException.CreateBDE(DBIERR_INVALIDHNDL); +// hNotify := hDBIObj(ANotify); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ClosePGNotify(var hNotify : hDBIObj) : DBIResult; +begin + try + {$IFDEF NEXTGEN} + TNativePGNotify(hNotify).DisposeOf; + {$ELSE} + TNativePGNotify(hNotify).Free; + {$ENDIF} + hNotify := nil; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.ListenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; +begin + try + TNativePGNotify(hNotify).ListenTo(pszEvent); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.UnlistenTo(hNotify : hDBIObj; pszEvent: string) : DBIResult; +begin + try + TNativePGNotify(hNotify).UnlistenTo(pszEvent); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.DoNotifyEx(hNotify: hDBIObj; pszChannel: string; pszPayload: string): DBIResult; +begin + try + TNativePGNotify(hNotify).DoNotifyEx(pszChannel, pszPayload); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.DoNotify(hNotify : hDBIObj; pszEvent: string) : DBIResult; +begin + try + TNativePGNotify(hNotify).DoNotify(pszEvent); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.CheckEvents(hNotify : hDBIObj; var Pid : Integer; var pszOutPut, pszPayload : String) : DBIResult; +begin + try + pszOutPut := TNativePGNotify(hNotify).CheckEvents(Pid, pszPayload); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetBackendPID(hDb: DAChDBIDb; var PID: Integer): DBIResult; +begin + try + Database := hDb; + PID := TNativeConnect(hDB).BackendPID; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + + +{****************************************************************************} +{ TNativePGNOTIFY } +{****************************************************************************} +constructor TNativePGNotify.Create(AConnect: TNativeConnect); +begin + FConnect := AConnect; + FHandle:=nil; +end; + +destructor TNativePGNotify.Destroy; +begin + inherited Destroy; +end; + +procedure TNativePGNotify.InternalExecute(Sql: string); +var + locResult : PPGResult; +begin + LocResult := _PQexecute(FConnect, SQL); + if Assigned(LocResult) then + PQclear(LocResult); +end; + +procedure TNativePGNotify.ListenTo(Event: string); +begin + Event := Trim(Event); + if Event <> '' then + InternalExecute('LISTEN ' + Event); +end; + +procedure TNativePGNotify.UnlistenTo(Event: string); +begin + Event := Trim(Event); + if Event <> '' then + InternalExecute('UNLISTEN ' + Event); +end; + +procedure TNativePGNotify.DoNotifyEx(Channel, Payload: string); +begin + Channel := Trim(Channel); + if Channel <> '' then + InternalExecute(Format('NOTIFY %s, %s', [Channel, QuotedStr(Payload)])); +end; + +procedure TNativePGNotify.DoNotify(Event: string); +begin + Event := Trim(Event); + if Event <> '' then + InternalExecute('NOTIFY ' + Event); +end; + +function TNativePGNotify.CheckEvents(var PID : Integer; var Payload: string): string; +begin + Result := ''; + if not Assigned(FConnect) or not (FConnect.FLoggin) then Exit; + PQconsumeInput(FConnect.Handle); + FHandle := PQnotifies(FConnect.Handle); + if Assigned(FHandle) then + begin + Result := FConnect.RawToString(FHandle^.relname); + Payload := FConnect.RawToString(FHandle^.extra); + PID := FHandle^.be_pid; + PQfreemem(FHandle); + end; +end; + +function TPSQLEngine.GetServerVersion(hDb: DAChDBIDb; + var ServerVersion: string): DBIResult; +begin + try + Database := hDb; + ServerVersion := TNativeConnect(hDb).GetServerVersion; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetUserProps(hDb: DAChDBIDb; const UserName: string; + var SuperUser, CanCreateDB, + CanUpdateSysCatalogs: boolean; var UserID: integer; + var ValidUntil: string):DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).GetUserProps(UserName, SuperUser, CanCreateDB, + CanUpdateSysCatalogs, UserID, ValidUntil); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetDBProps(hDB: DAChDBIDb; const DB: string; + var Owner, Tablespace: string; + var IsTemplate: boolean; + var DBOid: cardinal; var Comment: string):DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).GetDBProps(DB, Owner, Tablespace, + IsTemplate, DBOid, Comment); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + + +function TNativeConnect.GetServerVersion: string; +begin + if FServerVersion > '' then + begin + Result := FServerVersion; + Exit; + end; + + InternalConnect; + + Result := RawToString(PQparameterStatus(Handle, 'server_version')); + FServerVersion := Result; +end; + +function TNativeDataSet.FieldVal(FieldNo: Integer; FieldPtr : Pointer):String; +var + AField : TPSQLField; + Blank : Boolean; + Buff : array[0..MAX_BLOB_SIZE] of Char; + TimeStamp : TTimeStamp; + DateD : Double; +begin + Result :=''; + AField := Fields[FieldNo]; + AdjustNativeField(AField,FieldPtr,@Buff,Blank); + if not Blank then + case AField.FieldType of + fldINT16: Result := IntToStr(PSmallInt(@Buff)^); + fldUINT16: Result := IntToStr(PWord(@Buff)^); + fldINT32: Result := IntToStr(PLongInt(@Buff)^); + fldINT64: Result := IntToStr(PInt64(@Buff)^); + fldFLOAT: Result := SysUtils.FloatToStr(PDouble(@Buff)^); + //fldBOOL: Result := ifthen( PSmallInt(@Buff)^ > 0, 'T', 'F'); + fldZSTRING: + {$IFDEF DELPHI_12} + if FConnect.IsUnicodeUsed then + Result := PWideChar(@Buff) + else + {$ENDIF} + Result := String(PAnsiDACChar(@Buff)); + fldDATE : begin + TimeStamp.Date := PLongWord(@Buff)^; + TimeStamp.Time := 0; + Result := FormatDateTime('mm-dd-yyyy',SysUtils.Time+Trunc(TimeStampToDateTime(TimeStamp) + 1E-11), PSQL_FS); + end; + fldTIME : begin + TimeStamp.Time := PLongWord(@Buff)^; + TimeStamp.Date := DateDelta; + Result := FormatDateTime('hh:nn:ss',SysUtils.Date+TimeOf(TimeStampToDateTime(TimeStamp)), PSQL_FS); + end; + fldTIMESTAMP : + begin + DateD := PDouble(@Buff)^; + Result := FormatDateTime('mm-dd-yyyy hh:nn:ss',TimeStampToDateTime(MSecsToTimeStamp({$IFDEF FPC}Comp{$ENDIF}(DateD))), PSQL_FS); + end; + else + Result := string(DACAString(PAnsiDACChar(@Buff))); + end; +end; + + +procedure TNativeDataSet.GetRecordForKey(bDirectKey: Boolean; + iFields, iLen: integer; + pKey, pRecBuff: Pointer; + AStrictConformity: boolean = False); + + + procedure SetToLookupKey; + var + FieldPtr : Pointer; + FldNo : Integer; + Len : Integer; + R : Longint; + I : Integer; + AField : TPSQLField; + S : String; + Flds : array of Integer; + SFlds : array of String; + begin + S := ''; + Len := 0; + if bDirectKey then + begin + SetLength(Flds,FKeyDesc.iFldsinKey); + SetLength(SFlds,FKeyDesc.iFldsinKey); + for I := 0 to FKeyDesc.iFldsinKey-1 do + begin + FldNo := FKeyDesc.aiKeyFld[i]; + AField := Fields[FKeyDesc.aiKeyFld[i]]; + Flds[i] := FldNo - 1; + FieldPtr := pKey; + Inc(PAnsiDACChar(FieldPtr), Len); + SFlds[i] := FieldVal(FldNo, FieldPtr); + Inc(Len, AField.NativeSize + 1); // AField length in bytes + one byte null indicator + end; + end else + begin + SetLength(Flds,iFields); + SetLength(SFlds,iFields); + for I := 0 to iFields-1 do + begin + FldNo := FKeyDesc.aiKeyFld[I]; + AField := Fields[FKeyDesc.aiKeyFld[I]]; + Flds[I] := FldNo-1; + AField.Buffer := pKey; + SFlds[I] := FieldVal(FldNo, AField.FieldValue); + end; + end; + R := findrows(Flds,SFlds,False,iLen,AStrictConformity); + CheckParam(R=-1 ,DBIERR_RECNOTFOUND); + SettoSeqNo(R+1); + end; + + procedure SetToMasterKey; + var + FieldPtr : Pointer; + FldNo : Integer; + Len : Integer; + R : Longint; + I : Integer; + AField : TPSQLField; + S : String; + Flds : array of Integer; + SFlds : array of String; + begin + S := ''; + Len := 0; + SetLength(Flds,TNativeDataSet(MasterCursor).FKeyDesc.iFldsInKey); + SetLength(SFlds,TNativeDataSet(MasterCursor).FKeyDesc.iFldsInKey); + for I := 0 to TNativeDataSet(MasterCursor).FKeyDesc.iFldsInKey-1 do + begin + FldNo := TNativeDataSet(MasterCursor).FKeyDesc.aiKeyFld[I]; + AField := TNativeDataSet(MasterCursor).Fields[FldNo]; + Flds[I] := FldNo-1; + if bDirectKey then + begin + FieldPtr := pKey; + Inc(PAnsiDACChar(FieldPtr),Len + i); + SFlds[I] := S+FieldVal(FldNo, FieldPtr); + Inc(Len, AField.FieldLength); + end else + begin + AField.Buffer := pKey; + SFlds[i] := FieldVal(FldNo, AField.FieldValue); + end; + end; + R := TNativeDataSet(MasterCursor).findrows(Flds,SFlds,False,iLen,AStrictConformity); + CheckParam(R=-1 ,DBIERR_RECNOTFOUND); + TNativeDataSet(MasterCursor).SettoSeqNo(R+1); + end; + +begin + SetToLookupKey; + if MasterCursor <> nil then + SetToMasterKey; +end; + + +function TNativeDataSet.findrows(const Fields: array of Integer; + const SearchFields: array of String; ACaseSen: Boolean; + APartLen: Integer; AStrictConformity: boolean = False): int64; +var + I, K : Integer; + Cmp : Integer; + IsSorted: boolean; //05.05.2008 + + function Compare1(const S1: String; const S2 : String; FldType : integer):Integer; + + function CompWithLen(const P1, P2 : string): Integer; + begin + Result := Length(P1) - Length(P2); + if Result = 0 then + Result := CompareStr(P1, P2); + end; + + function CompWithoutLen(const P1, P2 : string): Integer; + begin + if AnsiSameStr(P1, Copy(P2, 1, Length(P1))) then + Result := 0 + else + Result := -1;//CompareStr(P1, P2); + end; + + function SqlDateToBDEDateTime(const Value: string): string; + var + Year, Month, Day: String; + Temp: string; + begin + Temp := Value; + Result := ''; + if Length(Temp) >= 10 then + begin + Year := Copy(Temp,1,4); + Month := Copy(Temp,6,2); + Day := Copy(Temp,9,2); + Result := Format('%s-%s-%s',[Month,Day,Year]); + Temp := Copy(Temp,12,8); + end; + if Length(Temp) >= 8 then + Result := Result + ' ' + Temp; + end; + + var BoolChar: string; + + begin + case FldType of + FIELD_TYPE_INT2, + FIELD_TYPE_INT4, + FIELD_TYPE_INT8, + FIELD_TYPE_OIDVECTOR, + FIELD_TYPE_OID: Result := CompWithLen(S1, S2); + + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8, + FIELD_TYPE_NUMERIC: if AStrictConformity then + Result := CompWithLen(StringReplace(S1, PSQL_FS.DecimalSeparator, + '.', [rfReplaceAll]), + S2) + else + Result := CompWithoutLen( + StringReplace(S1, PSQL_FS.DecimalSeparator, + '.', [rfReplaceAll]), + S2); + + FIELD_TYPE_DATE: + if AStrictConformity then + Result := CompWithLen(S1, DateTimeToStr(SqlDateToDateTime(S2, False){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})) + else + Result := CompWithoutLen(S1, DateTimeToStr(SqlDateToDateTime(S2, False){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})); + FIELD_TYPE_TIMESTAMP, + FIELD_TYPE_TIMESTAMPTZ: + if AStrictConformity then + Result := CompWithLen(S1, DateTimeToStr(SQLTimestampToDateTime(S2){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})) + else + Result := CompWithoutLen(S1, DateTimeToStr(SQLTimestampToDateTime(S2){$IFDEF DELPHI_7}, PSQL_FS{$ENDIF})); + + FIELD_TYPE_BOOL: begin + BoolChar := IfThen(S1 = '', 'F', 'T'); + Result := Ord(boolchar[1]) - Ord(UpCase(S2[START_STR_INDEX])); + end + + else + if AStrictConformity then + Result := CompWithLen(S1, S2) + else + Result := CompWithoutLen(S1, S2) + end + end; + + function FldVal(CurRow: integer; aIndex: Integer): String; + begin + if IsSorted then CurRow := FSortingIndex[CurRow]; //05.05.2008 + if (aIndex > -1) and (aIndex <= FieldCount-1) then //are we in range? + Result := FConnect.RawToString(PQGetValue(FStatement, CurRow, aIndex)) + else + Result := ''; + end; + +Var + P1,P2 : String; + +Begin + try + Cmp := -1; + IsSorted := IsSortedLocally; + for I := 0 to GetRecCount - 1 do + begin + Cmp := 0; + for K := 0 to High(Fields) do + begin + P1 := SearchFields[K]; + P2 := FldVal(I, Fields[K]); + if not ACaseSen then + begin + P1 := AnsiUpperCase(P1); + P2 := AnsiUpperCase(P2); + end; + Cmp := Cmp + Compare1(P1, P2, FieldType(Fields[K])); + if Cmp <> 0 then Break; + end; + if Cmp = 0 then Break; + end; + if Cmp = 0 then + Result := I + else + Result := -1; + except + Result := -1; + end; +end; + +function TNativeConnect.IsSSLUsed: boolean; +var P: pointer; +begin + Result := False; + if not FLoggin then Exit; + P := PQgetssl(FHandle); + Result := Assigned(P); +end; + +function TNativeConnect.IsTransactionActive: boolean; +begin + Result := FTransState = xsActive; +end; + + +procedure TNativeConnect.BeginBLOBTran; +var + Result: PPGresult; + TransParam: DACAString; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if (FTransState <> xsActive) + AND (GetTransactionStatus = trstIDLE) + then + begin + FBlobTransactionInProgress := True; + Result := PQexec(Handle, 'BEGIN /*BLOB handling*/'); + PQclear(Result); + TransParam := 'SET TRANSACTION ISOLATION LEVEL '; + case FTransLevel of + xilDIRTYREAD, + xilREADCOMMITTED : TransParam := TransParam + 'READ COMMITTED'; + xilREPEATABLEREAD: TransParam := TransParam + 'SERIALIZABLE'; + end; + Result := PQexec(Handle, {$IFNDEF NEXTGEN}PAnsiChar(TransParam){$ELSE}M.AsAnsi(TransParam).ToPointer{$ENDIF}); + PQclear(Result); + end +end; + +procedure TNativeConnect.RollbackBLOBTran; +var + Result: PPGresult; +begin + if FBlobTransactionInProgress AND + (GetTransactionStatus <> trstIDLE) then + begin + FBlobTransactionInProgress := False; + Result := PQexec(Handle, 'ROLLBACK /*BLOB handling*/'); + PQclear(Result); + end +end; + +procedure TNativeConnect.CommitBLOBTran; +var + Result: PPGresult; +begin + if FBlobTransactionInProgress AND + (GetTransactionStatus <> trstIDLE) then + begin + FBlobTransactionInProgress := False; + Result := PQexec(Handle, 'COMMIT /*BLOB handling*/'); + PQclear(Result); + end +end; + +procedure TNativeConnect.StoredProcList(pszWild: string; List: TStrings); +var + CRec : string; + I : LongInt; + sql : String; + RES : PPGresult; +begin + InternalConnect; + List.Clear; + Sql := 'SELECT p.oid, p.oid::regproc' + + ' FROM pg_proc p'; + if pszWild <> '' then + Sql := Sql + ' WHERE p.proname LIKE ' + QuotedStr(pszWild); + Sql := Sql + ' ORDER BY 2'; + RES := _PQexecute(Self, Sql); + if Assigned(RES) then + try + begin + for I := 0 to PQntuples(RES)-1 do + begin + CREC := RawToString(PQgetvalue(RES,I,1)); + RawToString(PQGetValue(Res,I,0)); + {$IFNDEF NEXTGEN} + List.AddObject(CREC,TOBject(strtoint(RawToString(PQGetValue(Res,I,0))))); + {$ELSE} + //this needs for eliminating "segmentation fault" during cast Integer to TObject + // (we increase ref for Integer - this is incorrect with ARC!) + List.AddObject(CREC, TObject( + TPListObject.Create(strtoint(RawToString(PQGetValue(Res,I,0)))) + )); + {$ENDIF} + end; + end; + finally + PQclear(RES); + end; +end; + +function TPSQLEngine.OpenStoredProcParams(hDb: DAChDBIDb; pszPName: string; + ProcOID: cardinal; List: TList{$IFDEF NEXTGEN}{$ENDIF}): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).StoredProcParams(pszPName, ProcOID, List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +procedure TNativeConnect.StoredProcParams(pszPName: string; ProcOID: cardinal; + List: TList{$IFDEF NEXTGEN}{$ENDIF}); +var + PDesc: ^SPParamDesc; + N: string; + I : LongInt; + ProcSchema, ProcName: string; + ArgNum: cardinal; + Sql : String; + MinOIDSel: string; + RES : PPGresult; + BdeType, BdeSubType: integer; + LogSize: integer; + LocArray: boolean; + Status: ExecStatusType; +const + sqlShowParameters = 'SELECT NULLIF(proargnames[g.s],''''), '+ //proargnames[g.s] > 8.0.0 + ' proargtypes[g.s], '+ + ' ''i''::varchar '+ + ' FROM '+ + ' pg_proc p, '+ + ' pg_type t JOIN pg_namespace nsp ON t.typnamespace = nsp.oid , '+ + ' generate_series(1,%d) as g(s) '+ //generate_series(...) > 8.0.0 + ' WHERE '+ + ' p.proargtypes[g.s] = t.oid AND '+ + ' p.oid = %d'; + + sqlShowParameters810 = 'SELECT NULLIF(proargnames[g.s],''''),'+ + ' CASE WHEN t.typtype = ''d''::char THEN' + + ' t.typbasetype' + + ' ELSE' + + ' t.oid' + + ' END,'+ + ' COALESCE(proargmodes[g.s], ''i'') '+ + ' FROM'+ + ' pg_proc p,'+ + ' pg_type t,'+ + ' generate_series(1,%d) as g(s)'+ + ' WHERE'+ + ' COALESCE(p.proargtypes[g.s-1],proallargtypes[g.s]) = t.oid AND'+ + ' p.oid = %d'+ + ' ORDER BY g.s '; +begin + InternalConnect; + List.Clear; + ArgNum := 0; + if GetserverVersionAsInt > 080100 then + MinOIDSel := 'SELECT GREATEST(pronargs, array_upper(proallargtypes,1)) ' + else + MinOIDSel := 'SELECT pronargs '; + if ProcOID = 0 then + begin + I := Pos('.',pszPName); + if I > 0 then + begin + ProcSchema := Copy(pszPName,1,I-1); + ProcSchema := StringReplace(ProcSchema, '"', '', [rfReplaceAll]); + ProcName := Copy(pszPName,I+1,MaxInt); + end + else + begin + ProcName := pszPName; + ProcSchema := '%'; + end; + ProcName := StringReplace(ProcName, '"', '', [rfReplaceAll]); + + MinOIDSel := MinOIDSel + ', pg_proc.oid '+ + 'FROM pg_catalog.pg_proc, pg_catalog.pg_namespace '+ + Format(' WHERE proname = ''%s'''+ + ' AND nspname LIKE ''%s''', + [ProcName,ProcSchema]) + + ' ORDER BY 2 '+ + ' LIMIT 1'; + end + else + MinOIDSel := MinOIDSel + + ' FROM pg_catalog.pg_proc '+ + Format(' WHERE oid = %d', [ProcOID]); + + + + RES := _PQexecute(Self, MinOIDSel); + Status := PQresultStatus(RES); + if (Status = PGRES_TUPLES_OK) and (PQntuples(RES) > 0) then + begin + ArgNum := StrToInt(RawToString(PQgetvalue(RES,0,0))); + if ProcOID = 0 then + ProcOID := StrToIntDef(RawToString(PQgetvalue(RES,0,1)), InvalidOID); + end + else + CheckResult(); + PQclear(Res); + + if ProcOID * ArgNum = 0 then Exit; + + + if GetserverVersionAsInt >= 080100 then + Sql := Format(sqlShowParameters810,[ArgNum,ProcOID]) + else + Sql := Format(sqlShowParameters,[ArgNum,ProcOID]); + + RES := _PQExecute(Self, Sql); + if PQresultStatus(RES) = PGRES_TUPLES_OK then + begin + for I := 0 to PQntuples(RES)-1 do + begin + New(PDesc); + {$IFNDEF NEXTGEN} + ZeroMemory(PDesc,SizeOf(PDesc^)); + {$ELSE} + FillChar(PDesc^,SizeOf(PDesc^), 0); + {$ENDIF} + + if (PQgetisnull(RES,I,0) = 1) then + N := 'arg' + IntToStr(I) + else + N := RawToString(PQgetvalue(RES,I,0)); + PDesc^.szName := N; + PDesc^.uParamNum := I; + FieldMapping(StrToIntDef(RawToString(PQgetvalue(RES,I,1)), InvalidOID), 0, BdeType, BdeSubType, LogSize, LocArray); + PDesc^.uFldType := BdeType; + PDesc^.uSubType := BdeSubType; + N := RawToString(PQgetvalue(RES,I,2)); + case N[START_STR_INDEX] of + 'o': PDesc^.eParamType := paramOUT; + 'b': PDesc^.eParamType := paramINOUT; + else + PDesc^.eParamType := paramIN; + end; + List.Add(PDesc) + end; + end; + PQclear(RES); +end; + +function TNativeConnect.StringToRaw(S: string): PAnsiDACChar; +var + _S: DACAString; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + _S := StringToRawS(S); + DACAllocStr(Result, Length(S)); + DACStrCopy(Result, {$IFNDEF NEXTGEN} + PAnsiDACChar(_S) + {$ELSE} + M.AsAnsi(_S).ToPointer + {$ENDIF} + ); +end; + +function TNativeConnect.StringToRawS(S: string): DACAString; +begin + if IsUnicodeUsed then + Result := {$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(S)) + else + Result := DACAString(S); +end; + +function TPSQLEngine.QPrepareProc(hDb: DAChDBIDb; pszProc: PChar; + hParams: pointer; var hStmt: hDBIStmt): DBIResult; +var SQLText,ParStr: string; + i: integer; + aParams: TPSQLParams; +begin + try + aParams := TPSQLParams(hParams); + QAlloc(hDb, hStmt); + SQLText := 'SELECT * FROM '+pszProc+'(%s)'; + if (aParams.Count > 0) and (aParams[0].ParamType in [ptInput,ptInputOutput]) then + ParStr := ':' + AnsiQuotedStr(aParams[0].Name, '"') + else + ParStr := ''; + for i := 1 to aParams.Count - 1 do + if aParams[i].ParamType in [ptInput,ptInputOutput] then + ParStr := ParStr + ', :' + AnsiQuotedStr(aParams[i].Name, '"'); + TNativeDataSet(hStmt).SQLQuery := Format(SQLText,[ParStr]); + TNativeDataSet(hStmt).isQuery := True; // PaGo 24.07.2007 + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.QSetProcParams(hStmt: hDBIStmt; Params: TParams): DBIResult; +begin + try + TNativeDataSet(hStmt).StoredProcSetParams(Params); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +procedure TNativeDataSet.StoredProcSetParams(Params: TParams); +begin + QuerySetParams(Params,SQLQuery); +end; + + +procedure TNativeConnect.SetCharSet(var ACharSet: string); +{$IFDEF NEXTGEN} +var + M: TMarshaller; +{$ENDIF} +begin + if (ACharSet = '') or not FLoggin then + begin + ACharSet := GetCharSet(); + Exit; + end; + + PQsetClientEncoding(Handle, {$IFNDEF NEXTGEN} + PAnsiChar(AnsiString(ACharSet)) + {$ELSE} + M.AsAnsi(ACharSet).ToPointer + {$ENDIF} + ); + + ACharSet := GetCharSet(); + + {$IFDEF M_DEBUG} + LogDebugMessage('INFO', 'Encoding changed to ' + ACharset); + {$ENDIF} +end; + +procedure TNativeConnect.GetCharSetList(var List: TStrings); +var + sql : String; + RES : PPGresult; + i: integer; + CREC: string; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + InternalConnect; + List.Clear; + Sql := Format('SELECT pg_encoding_to_char(num.n) FROM generate_series(0,%d) as num(n)', [MAX_ENCODING_ID]); + RES := PQexec(Handle, {$IFNDEF NEXTGEN} + PAnsiChar(AnsiString(Sql)) + {$ELSE} + M.AsAnsi(Sql).ToPointer + {$ENDIF} + ); + if Assigned(RES) then + try + CheckResult; + List.BeginUpdate; + try + for i:=0 to PQntuples(RES)-1 do + begin + CREC := Trim(RawToString(PQgetvalue(RES,i,0))); + if CREC > '' then List.Append(CREC); + end; + finally + List.EndUpdate; + end; + finally + PQclear(RES); + end; +end; + +function TPSQLEngine.SetCharacterSet(hDb: DAChDBIDb; var CharSet: string): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).SetCharSet(CharSet); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetErrorVerbosity(hDb : DAChDBIDb; const ErrorVerbosity: TErrorVerbosity): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).SetErrorVerbosity(ErrorVerbosity); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SetCommandTimeout(hDb : DAChDBIDb; var Timeout : cardinal):DBIResult; +begin + try + Database := hDb; + Timeout := TNativeConnect(hDb).SetTimeout(Timeout); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetCharacterSets(hDb: DAChDBIDb; + List: TStrings): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).GetCharSetList(List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TNativeDataSet.CheckUniqueKey(var KeyNumber : integer): Boolean; +var + I: Integer; + Item : TPSQLIndex; +begin + Result := False; + for I := 1 to FindexDescs.Count do + begin + Item := FIndexDescs.mIndex[I]; + if Item.Primary or Item.Unique then + begin + Result := True; + KeyNumber := I; + Break; + end; + end; +end; + +function TNativeDataSet.GetLastInsertID(const KeyNumber: integer): integer; +var + S: string; + i: integer; + RES: PPGresult; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + Result := -1; + S := FFieldDescs.Field[KeyNumber+1].FieldDefault; + i := Pos('nextval(',lowercase(S)); + if i>0 then + S := StringReplace(S, 'next', 'curr', [rfReplaceAll]) + else + Exit; + S := 'SELECT ' + S; + Res := PQExec(FConnect.Handle, {$IFNDEF NEXTGEN} + PAnsiChar(AnsiString(S)) + {$ELSE} + M.AsAnsi(S).ToPointer + {$ENDIF} + ); + if Assigned(RES) then + try + FConnect.CheckResult; + if PQntuples(RES)>0 then + Result := StrToIntDef(FConnect.RawToString(PQgetvalue(RES,0,0)),-1); + finally + PQclear(RES); + end; +end; + +function TNativeConnect.GetTransactionStatus: TTransactionStatusType; +begin + Result := PQTransactionStatus(FHandle); +end; + +procedure TNativeConnect.GetUserProps(const UserName: string; + var SuperUser, CanCreateDB, + CanUpdateSysCatalogs: boolean; var UserID: integer; + var ValidUntil: string); +var + sql : String; + RES : PPGresult; +begin + SuperUser := False; + CanCreateDB := False; + CanUpdateSysCatalogs := False; + UserID := -1; + ValidUntil := ''; + InternalConnect; + Sql := 'SELECT usesysid, usecreatedb, usesuper, usecatupd, valuntil '+ + ' FROM pg_user WHERE usename = '''+UserName+''''; + + RES := _PQExecute(Self, Sql); + if Assigned(RES) then + try + CheckResult; + if PQntuples(RES) > 0 then + begin + UserID := StrToIntDef(string(PQgetvalue(RES, 0, 0)), InvalidOID); + CanCreateDB := PQgetvalue(RES, 0, 1) = 't'; + SuperUser := PQgetvalue(RES, 0, 2) = 't'; + CanUpdateSysCatalogs := PQgetvalue(RES, 0, 3) = 't'; + ValidUntil := string(PQgetvalue(RES, 0, 4)); + end; + finally + PQclear(RES); + end; +end; + + +procedure TNativeConnect.GUCList(List: TStrings); +var + I: Longint; + Res: PPGresult; +begin + if not FLoggin then Exit; + List.Clear; + Res := PQExec(Handle, 'SHOW ALL'); + try + for I := 0 to PQntuples(Res) - 1 do + List.Values[RawToString(PQgetvalue(Res, I, 0))] := RawToString(PQgetvalue(Res, I, 1)); + finally + PQclear(Res); + end; +end; + +procedure TNativeConnect.GetDBProps(const DB: string; var Owner, + Tablespace: string; var IsTemplate: boolean; var DBOid: cardinal; + var Comment: string); +var + sql : String; + RES : PPGresult; + SV: integer; +begin + DBOid := 0; + IsTemplate := False; + Tablespace := ''; + Owner := ''; + SV := GetServerVersionAsInt; + + if SV >= 080200 then + Sql := Format(' LEFT JOIN %s ON (db.oid = %s.objoid), ', ['pg_shdescription', 'pg_shdescription']) + else + Sql := Format(' LEFT JOIN %s ON (db.oid = %s.objoid), ', ['pg_description', 'pg_description']); + + Sql := 'SELECT db.oid, datistemplate, usename, %s '+ + ' COALESCE(description,'''')::varchar '+ + ' FROM pg_database as db '+ + Sql + + ' %s '+ + ' pg_user as sh '+ + ' WHERE '+ + ' %s '+ + ' sh.usesysid = datdba AND '+ + ' db.datname = '''+DB+''''; + + + + if SV >= 080000 then + Sql := Format(Sql,['spcname,','pg_tablespace as tsp,','tsp.oid = dattablespace AND']) + else + Sql := Format(Sql,['','','']); + + RES := _PQExecute(Self, Sql); + if Assigned(RES) then + try + CheckResult; + if PQntuples(RES) > 0 then + begin + DBOid := StrToInt64(string(PQgetvalue(RES,0,0))); + IsTemplate := PQgetvalue(RES,0,1) = 't'; + Owner := RawToString(PQgetvalue(RES,0,2)); + if SV >= 080000 then + Tablespace := RawToString(PQgetvalue(RES,0,3)); + Comment := RawToString(PQgetvalue(RES,0,4)); + end; + finally + PQclear(RES); + end; +end; + + +procedure TNativeConnect.GetTableProps(const TableName: string; var Owner, + Comment, Tablespace: string; var TableOid: cardinal); +var + sql, Tbl, Schema : String; + I : integer; + RES : PPGresult; +begin + Owner := ''; + Comment := ''; + Tablespace := ''; + TableOid := 0; + + Sql := 'SELECT pg_class.oid, usename, '#13#10 + + ' COALESCE(pg_description.description,''''), COALESCE(pg_tablespace.spcname,'''')'#13#10 + + ' FROM pg_class'#13#10 + + ' INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)'#13#10 + + ' INNER JOIN pg_user ON (pg_class.relowner = pg_user.usesysid)'#13#10 + + ' LEFT JOIN pg_tablespace ON (pg_class.reltablespace = pg_tablespace.oid)'#13#10 + + ' LEFT JOIN pg_description ON (pg_description.objoid = pg_class.oid)'#13#10 + + ' WHERE relkind IN (''r'', ''v'') AND relname = ''%s'' AND nspname LIKE ''%s'''; + + + Tbl := StringReplace(TableName,'"','',[rfReplaceAll]); + I := Pos('.',Tbl); + if I > 0 then + begin + Schema := Copy(Tbl, 1, I-1); + Tbl := Copy(Tbl, I+1, MaxInt); + end else + Schema := '%'; + Sql := Format(Sql, [Tbl, Schema]); + RES := _PQExecute(Self, Sql); + try + if (PQresultStatus(RES) = PGRES_TUPLES_OK) and (PQntuples(RES) > 0) then + begin + TableOid := StrToInt64(RawToString(PQgetvalue(RES, 0, 0))); + Owner := RawToString(PQgetvalue(RES, 0, 1)); + Comment := RawToString(PQgetvalue(RES, 0, 2)); + Tablespace := RawToString(PQgetvalue(RES, 0, 3)); + end; + finally + PQclear(RES); + end; +end; + +function TPSQLEngine.GetTableProps(hDB: DAChDBIDb; const TableName: string; + var Owner, Comment, Tablespace: string; var TableOid: cardinal): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).GetTableProps(TableName, Owner, Comment, + Tablespace, TableOid); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TNativeConnect.GetServerVersionAsInt: integer; +begin + if FIntServerVersion <= 0 then + FIntServerVersion := PQserverVersion(Handle); + Result := FIntServerVersion +end; + + +function TPSQLEngine.GetFieldOldValue(hCursor: hDBICur; AFieldName: string; AParam: TParam): DBIResult; +begin + try + TNativeDataSet(hCursor).FieldOldValue(AFieldName, AParam); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.GetFieldValueFromBuffer(hCursor: hDBICur; + PRecord: Pointer; AFieldName: string; AParam: TParam; const UnchangedAsNull: boolean): DBIResult; +begin + try + TNativeDataSet(hCursor).FieldValueFromBuffer(PRecord, AFieldName, AParam, UnchangedAsNull); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + + +procedure TNativeDataSet.FieldOldValue(AFieldName: string; var AParam: TParam); +var AFNum, Len: integer; + FVal: PAnsiDACChar; +begin + AFNum := FieldIndex(AnsiQuotedStr(AFieldName, '"')); + if AFNum = -1 then Exit; + if FieldIsNull(AFNum) then + begin + AParam.Value := Null; + Exit; + end; + case FieldType(AFNum) of + FIELD_TYPE_BYTEA: + begin + FVal := PQUnescapeBytea(FieldBuffer(AFNum),Len); + try + {$IFDEF DELPHI_17} + AParam.SetBlobData(BytesOf({$IFDEF NEXTGEN}String{$ENDIF}(FVal[0])), Len); + {$ELSE} + AParam.SetBlobData(FVal, Len); + {$ENDIF} + TPSQLParam(AParam).DataTypeOID := FIELD_TYPE_BYTEA; + finally + PQFreeMem(FVal); + end; + end; + FIELD_TYPE_BOOL: AParam.AsBoolean := SameText('t', Field(AFNum)); + else + AParam.Value := Field(AFNum); + end; + if FieldType(AFNum) = FIELD_TYPE_OID then TPSQLParam(AParam).DataTypeOID := FIELD_TYPE_OID; +end; + + +procedure TNativeDataSet.FieldValueFromBuffer(PRecord: Pointer; AFieldName: string; + var AParam: TParam; const UnchangedAsNull: boolean); +var + I : Integer; + Fld : TPSQLField; + Src : Pointer; + +begin + + for I := 1 to FFieldDescs.Count do + begin + Fld := FFieldDescs.Field[I]; + Fld.Buffer:= PRecord; + if CompareText(Fld.FieldName, AFieldName)<>0 then Continue; + Src := Fld.FieldValue; + Inc(PAnsiDACChar(Src)); + if not Fld.FieldChanged and not UnchangedAsNull then //field was not changed, we put there old value + begin + FieldOldValue(AFieldName, AParam); + Exit; + end; + if Fld.FieldNull then + AParam.Value := Null + else + begin + case Fld.FieldType of + fldBOOL: AParam.AsBoolean := Boolean(SmallInt(Src^)); + fldINT16: AParam.AsSmallInt := SmallInt(Src^); + fldINT32: AParam.AsInteger := LongInt(Src^); + fldINT64: begin + {$IFDEF DELPHI_5} + AParam.AsString := IntToStr(Int64(Src^)); + {$ELSE} + AParam.DataType := DataTypeMap[Fld.FieldType]; + AParam.Value := Int64(Src^); + {$ENDIF} + end; + fldFLOAT: AParam.AsFloat := Double(Src^); + fldZSTRING: begin + {$IFDEF DELPHI_12} + if FConnect.IsUnicodeUsed then + AParam.AsString := PWideChar(Src) + else + {$ENDIF} + AParam.AsString := String(PAnsiDACChar(Src)); + if (Fld.NativeType = FIELD_TYPE_BIT) or (Fld.NativeType = FIELD_TYPE_VARBIT) then + AParam.AsString := 'B' + AParam.AsString; + end; + fldUUID: AParam.AsString := String(PAnsiDACChar(Src)); + fldBLOB: if Fld.NativeBLOBType = nbtOID then + begin + AParam.AsInteger := StrToUInt(BlobValue(Src, Fld)); + TPSQLParam(AParam).DataTypeOID := FIELD_TYPE_OID; + end + else + if not Assigned(TBlobItem(Src^).Blob) or (TBlobItem(Src^).Blob.Size = 0) then + AParam.Value := Null + else + if Fld.FieldSubType = fldstMemo then + AParam.AsString := MemoValue(Src, False) + else + AParam.LoadFromStream(TBlobItem(Src^).Blob, ftBlob); + fldDate: AParam.AsDate := TDateTime(Src^); + fldTime: AParam.AsTime := TDateTime(Src^); + fldTIMESTAMP: AParam.AsDateTime := TDateTime(Src^); + {$IFDEF DELPHI_12} + fldFMTBCD: AParam.AsFMTBCD := TBcd(Src^); + {$ENDIF} + end; //case + end; //else + Break; + end; +end; + +function TNativeConnect.GetTimeout: cardinal; +var + RES : PPGresult; +begin + Result := 0; + if GetserverVersionAsInt <= 070302 then + Exit; + InternalConnect; + RES := PQexec(Handle, 'SELECT current_setting(''statement_timeout'')'); + if Assigned(RES) then + try + CheckResult; + if PQntuples(RES) > 0 then + Result := StrToIntDef(RawToString(PQgetvalue(RES,0,0)),0); + finally + PQclear(RES); + end; +end; + +{$HINTS OFF} +procedure TNativeConnect.SetErrorVerbosity(const ErrorVerbosity: TErrorVerbosity); +var OldEV: TErrorVerbosity; +{$IFDEF M_DEBUG} +const EVNames: array[TErrorVerbosity] of DACAString = ('TERSE', 'DEFAULT', 'VERBOSE'); +{$ENDIF} +begin + if FLoggin then + begin + OldEV := PQsetErrorVerbosity(Handle, ErrorVerbosity); + {$IFDEF M_DEBUG} + LogDebugMessage('INFO', Format('Error verbosity changed from %s to %s', [EVNames[OldEV], EVNames[ErrorVerbosity]])); + {$ENDIF} + end; +end; +{$HINTS ON} + +function TNativeConnect.SetTimeout(const Timeout: cardinal): cardinal; +var + S : String; + RES : PPGresult; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + Result := 0; + if GetserverVersionAsInt <= 070302 then + Exit; + InternalConnect; + S := Format('SELECT set_config(''statement_timeout'', ''%d'', false)',[Timeout]); + RES := PQexec(Handle, {$IFNDEF NEXTGEN} + PAnsiChar(AnsiString(S)) + {$ELSE} + M.AsAnsi(S).ToPointer + {$ENDIF} + ); + if Assigned(RES) then + try + CheckResult; + Result := StrToIntDef(string(PQgetvalue(Res, 0, 0)), Timeout); + finally + PQclear(RES); + end; +end; + +function TPSQLEngine.GetCommandTimeout(hDb: DAChDBIDb; + var Timeout: cardinal): DBIResult; +begin + try + Database := hDb; + Timeout := TNativeConnect(hDb).GetTimeout; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + + +function TNativeDataSet.HasFieldTimeZone(const FldNum: integer): boolean; +begin + case FieldType(FldNum-1) of + FIELD_TYPE_TIMETZ, + FIELD_TYPE_TIMESTAMPTZ: Result := True; + else + Result := False; + end; +end; + +function TNativeDataSet.FieldTable(FieldNum: integer): cardinal; +begin + if FStatement <> nil then + Result := PQftable(FStatement,FieldNum) + else + Result := InvalidOid; +end; + +function TNativeDataSet.FieldOrigin(FieldNum: integer): string; +var TabOid: cardinal; + ColNum: integer; + s: string; + IsOK: boolean; +begin + Result := ''; + if FStatement <> nil then + begin + TabOid := FieldTable(FieldNum - 1); //pg_attribute uses 1 as first field index, but low level rotines not + if TabOid <= InvalidOid then Exit; + ColNum := FieldPosInTable(FieldNum - 1); + s := Format('SELECT %u::regclass || ''.'' || quote_ident(attname) '+ + 'FROM pg_attribute WHERE attrelid = %u AND attnum = %d', + [TabOid, TabOid, ColNum]); + Result := FConnect.SelectStringDirect(s, IsOK, 0); + end; +end; + +function TNativeDataSet.FieldPosInTable(FieldNum: integer): Integer; +begin + if FStatement <> nil then + begin + Result := PQftablecol(FStatement,FieldNum); + if Result = 0 then + Result := -1; + end + else + Result := -1; +end; + +function TPSQLEngine.GetLastInsertId(hCursor: hDBICur; + const FieldNum: integer; var ID: integer): DBIResult; +begin + try + ID := TNativeDataset(hCursor).GetLastInsertID(FieldNum); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +procedure TNativeDataSet.SortBy(FieldNames: string; + Compare: TPSQLDatasetSortCompare); +begin + FCustomCompareFunc := Compare; + try + SortBy(FieldNames); + finally + FCustomCompareFunc := nil; + end; +end; + +procedure TNativeDataSet.SortBy(FieldNames: string); +var + a, cnt, i : integer; + str : string; + AFields : array of integer; + IsReverseOrder : array of boolean; +const + sAsc : string = ' ASC'; + sDesc : string = ' DESC'; +begin + FSortingFields := Trim(FieldNames); + + if FSortingFields = '' then + begin + SetLength(FSortingIndex,0); + Exit; + end; + + cnt := 0; + + for i:=1 to Length(FieldNames) do + begin + if FieldNames[i] = ',' then Inc(cnt);//count number of AFields + + if FieldNames[i] = #9 then + FieldNames[i] := ' ';//replace TABs to SPACEs + end; + + SetLength(AFields, cnt + 1); + SetLength(IsReverseOrder, cnt + 1); + + i := 0; + if cnt > 0 then//multi-AFields sorting + while Pos(',', FieldNames) <> 0 do + begin + a := Pos(',', FieldNames); + str := Trim(copy(FieldNames, 1, a - 1)); + Delete(FieldNames, 1, a); + + if AnsiUpperCase(copy(str, Length(str) - Length(sDesc) + 1, Length(sDesc))) = sDesc then + begin + IsReverseOrder[i] := true; + Delete(str, Length(str) - Length(sDesc) + 1, Length(sDesc)); + end + else if AnsiUpperCase(copy(str, Length(str) - Length(sAsc) + 1, Length(sAsc))) = sAsc then + begin + IsReverseOrder[i] := false; + Delete(str, Length(str) - Length(sAsc) + 1, Length(sAsc)); + end + else + begin + IsReverseOrder[i] := false; + end; + + a := FieldIndex(Trim(str));//trying to find dield in AFields definitions + if a = -1 then + begin + DatabaseError(Format(SFieldNotFound, [Str])); + FSortingFields := ''; + SetLength(FSortingIndex,0); + end; + AFields[i] := a; + Inc(i); + end; + + //single field sorting (or last field sorting) + str := Trim(FieldNames); + + if AnsiUpperCase(copy(str, Length(str) - Length(sDesc) + 1, Length(sDesc))) = sDesc then + begin + IsReverseOrder[i] := true; + Delete(str, Length(str) - Length(sDesc) + 1, Length(sDesc)); + end + else if AnsiUpperCase(copy(str, Length(str) - Length(sAsc) + 1, Length(sAsc))) = sAsc then + begin + IsReverseOrder[i] := false; + Delete(str, Length(str) - Length(sAsc) + 1, Length(sAsc)); + end + else + begin + IsReverseOrder[i] := false; + end; + Str := Trim(str); + a := FieldIndex(str);//trying to find field in AFields definitions + if a = -1 then + begin + DatabaseError(Format(SFieldNotFound, [str])); + FSortingFields := ''; + SetLength(FSortingIndex,0); + end; + AFields[i] := a; + + InternalSortBy(AFields, IsReverseOrder); +end; + +procedure TNativeDataSet.InternalSortBy(const Fields: array of Integer; + const IsReverseOrder: array of boolean); + +var aRecNum: integer; + i: integer; + + function StringToVariant(S: string; NativeType: oid): variant; + begin + case NativeType of + FIELD_TYPE_INT2, + FIELD_TYPE_INT4, + FIELD_TYPE_INT8: + {$IFDEF DELPHI_5} + Result := StrToIntDef(S, 0); + {$ELSE} + Result := StrToInt64Def(S, 0); + {$ENDIF} +{$IFDEF UNDER_DELPHI_12} + FIELD_TYPE_NUMERIC, +{$ENDIF} + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: + try + Result := StrToFloat(S, PSQL_FS); + except + //D5 have no StrToFloatDef + on E: EConvertError do + Result := 0.0; + end; +{$IFDEF DELPHI_12} + FIELD_TYPE_NUMERIC: + Result := VarFMTBcdCreate(S, High(Word), High(Word)); +{$ENDIF} + +FIELD_TYPE_BOOL: Result := S[START_STR_INDEX] = 't'; + + FIELD_TYPE_OID: if dsoOIDAsInt in FOptions then + {$IFDEF DELPHI_5} + Result := StrToIntDef(S, InvalidOid) + {$ELSE} + Result := StrToInt64Def(S, InvalidOid) + {$ENDIF} + else + Result := 0; + FIELD_TYPE_TEXT, + FIELD_TYPE_BYTEA: Result := 0; //BLOB's are not comparable + + else + //datetime fields will be compared here also + //cause we have ISO output datestyle: yyyy-mm-dd hh:mm:ss[-tz] + Result := S; + end; + + end; + + function CustomCmpRecords(Index1, Index2: integer): integer; + var + V1, V2: Variant; + Idx1IsNull, Idx2IsNull, i: integer; + begin + Result := 0; + for i:= Low(Fields) to High(Fields) do + begin + Idx1IsNull := PQGetIsNull(FStatement, FSortingIndex[Index1], Fields[I]); + Idx2IsNull := PQGetIsNull(FStatement, FSortingIndex[Index2], Fields[I]); + if Idx1IsNull + Idx2IsNull = 2 then Exit; //no need to compare two NULLs + if Idx1IsNull = 1 then + V1 := Null + else + V1 := StringToVariant(FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index1], Fields[I])), + PQFType(FStatement, Fields[I])); + if Idx2IsNull = 1 then + V2 := Null + else + V2 := StringToVariant(FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index2], Fields[I])), + PQFType(FStatement, Fields[I])); + Result := FCustomCompareFunc(nil, V1, V2, Fields[I]); + if Result <> 0 then Break; + end; + end; + + function DefaultCmpRecords(Index1, Index2: integer): integer; + var i, Idx1IsNull, Idx2IsNull: integer; + FVal1, FVal2: string; + begin + Result := 0; + for i:= Low(Fields) to High(Fields) do + begin + Idx1IsNull := PQGetIsNull(FStatement, FSortingIndex[Index1], Fields[I]); + Idx2IsNull := PQGetIsNull(FStatement, FSortingIndex[Index2], Fields[I]); + case Idx1IsNull + Idx2IsNull of + 2: Result := 0; + 1: Result := Idx1IsNull - Idx2IsNull; + else + begin + FVal1 := FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index1], Fields[I])); + FVal2 := FConnect.RawToString(PQGetValue(FStatement, FSortingIndex[Index2], Fields[I])); + case PQFType(FStatement, Fields[I]) of + FIELD_TYPE_INT2, + FIELD_TYPE_INT4, + FIELD_TYPE_INT8: Result := StrToInt64Def(FVal1, 0) - + StrToInt64Def(FVal2, 0); +{$IFDEF UNDER_DELPHI_12} + FIELD_TYPE_NUMERIC, +{$ENDIF} + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: + try + Result := Sign(StrToFloat(FVal1, PSQL_FS) - + StrToFloat(FVal2, PSQL_FS)); + except + //D5 have no StrToFloatDef + on E: EConvertError do + Result := 0; + end; +{$IFDEF DELPHI_12} + FIELD_TYPE_NUMERIC: Result := BcdCompare(StrToBcd(FVal1, PSQL_FS), StrToBcd(FVal2, PSQL_FS)); +{$ENDIF} + FIELD_TYPE_BOOL: Result := ord(FVal1[START_STR_INDEX]) - + ord(FVal2[START_STR_INDEX]); + + FIELD_TYPE_OID: if dsoOIDAsInt in FOptions then + Result := StrToIntDef(FVal1, InvalidOid) - + StrToIntDef(FVal2, InvalidOid) + else + Result := 0; + FIELD_TYPE_TEXT, + FIELD_TYPE_BYTEA: Result := 0; //BLOB's are not comparable + + else + //datetime fields will be compared here also + //cause we have ISO output datestyle: yyyy-mm-dd hh:mm:ss[-tz] + Result := AnsiCompareStr(FVal1, FVal2); + end; + end; + end; + if IsReverseOrder[i] then + Result := -Result; + if Result <> 0 then Break; + end; + end; + + procedure SwapIndexes(Index1, Index2: integer); + var T: integer; + begin + T := FSortingIndex[Index1]; + FSortingIndex[Index1] := FSortingIndex[Index2]; + FSortingIndex[Index2] := T; + end; + + function CmpRecords(Index1, Index2: integer): integer; + begin + if not Assigned(FCustomCompareFunc) then + Result := DefaultCmpRecords(Index1, Index2) + else + Result := CustomCmpRecords(Index1, Index2); + end; + + procedure QuickSort(L, R: Integer); + var + I, J, P: Integer; + begin + repeat + I := L; + J := R; + P := (L + R) shr 1; + repeat + while CmpRecords(I, P) < 0 do + Inc(I); + while CmpRecords(J, P) > 0 do + Dec(J); + if I <= J then + begin + SwapIndexes(I, J); + if P = I then + P := J + else if P = J then + P := I; + Inc(I); + Dec(J); + end; + until I > J; + if L < J then QuickSort(L, J); + L := I; + until I >= R; + end; + +begin + aRecNum := GetRecCount; + if (High(Fields) = -1) or (aRecNum < 2) then + Exit; + SetLength(FSortingIndex, aRecNum); + + for i := 0 to aRecNum - 1 do //initialization + FSortingIndex[i] := i; + + QuickSort(Low(FSortingIndex), High(FSortingIndex)); +end; + +function TNativeDataSet.GetRecNo: integer; +var + nGetRecCount: Integer; +begin + Result := RecNo; + if not IsSortedLocally then Exit; + nGetRecCount := GetRecCount(); + if nGetRecCount > 0 then + if (High(FSortingIndex) = nGetRecCount-1) then Result := FSortingIndex[RecNo]; +end; + +function TNativeDataSet.IsSortedLocally: boolean; +begin + Result := (High(FSortingIndex) > -1) and (High(FSortingIndex) = GetRecCount-1); //19.05.2008 +end; + +procedure TPSQLIndexes.SetNeedUpdate(const Value: boolean); +begin + FUpdated := Value; +end; + +function TNativeDataSet.CheckCanLive: boolean; +var i: integer; + TabOID: cardinal; +begin + + Result := False; + + if FConnect = nil then + exit; + + if not IsQuery then //assume tables are editable by default + begin + Result := True; + Exit; + end; + + TabOID := FieldTable(0); + if TabOID = InvalidOid then Exit; + for i:=1 to FieldCount-1 do + if (TabOID = InvalidOid) or (TabOID <> FieldTable(i)) then + Exit + else + TabOID := FieldTable(i); + + Result := True; //all checks passed +end; + +//>> pasha_golub 10.08.06 +function TPSQLEngine.CheckBuffer(hCursor: hDBICur; + PRecord: Pointer): DBIResult; +begin + try + if TNativeDataSet(hCursor).FCurrentBuffer = PRecord then + TNativeDataSet(hCursor).FCurrentBuffer:= nil; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; +//<< pasha_golub 10.08.06 + +function TPSQLEngine.OpenTablespaceList(hDb: DAChDBIDb; pszWild: string; + List: TStrings): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).TablespaceList(pszWild, List); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + + +procedure TNativeConnect.TablespaceList(pszWild: string; List: TStrings); +var + CRec : string; + I : LongInt; + sql : String; + RES : PPGresult; +begin + InternalConnect; + Sql := 'SELECT spcname '+ + ' FROM "pg_tablespace" '; + if pszWild <> '' then + Sql := Sql + ' WHERE spcname LIKE ' + AnsiQuotedStr(pszWild,''''); + Sql := Sql + ' ORDER BY 1'; + RES := _PQExecute(Self, Sql); + try + if Assigned(RES) then + begin + CheckResult; + for I := 0 to PQntuples(RES)-1 do + begin + CRec := RawToString(PQgetvalue(RES,I,0)); + List.Add(CRec); + end; + end; + finally + PQclear(RES); + end; +end; + +function TNativeDataSet.UuidValue(P : Pointer; NeedQuote: boolean = True): string; +var AVal: PAnsiDACChar; +begin + Result := ''; + if not Assigned(P) then Exit; + AVal := PAnsiDACChar(P); + Result := FConnect.RawToString(AVal); + if NeedQuote then Result := '''' + Result + ''''; +end; + +function TNativeDataSet.StrValue(P : Pointer; NeedQuote: boolean = True):String; +var + Buffer, AVal : PAnsiDACChar; + SZ, Err : Integer; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + Result := ''; + if P <> nil then + begin + {$IFDEF DELPHI_12} + if FConnect.IsUnicodeUsed then + {$IFNDEF NEXTGEN} + AVal := PAnsiDACChar(FConnect.StringToRawS(PWideChar(P))) + {$ELSE} + AVal := M.AsAnsi(FConnect.StringToRawS(PWideChar(P))).ToPointer + {$ENDIF} + else + {$ENDIF} + AVal := PAnsiDACChar(P); + + if not NeedQuote then + begin + Result := FConnect.RawToString(AVal); + Exit; + end; + + {$IFNDEF NEXTGEN} + SZ := {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrLen(AVal); + {$ELSE} + SZ := Length(MarshaledAString(@AVal)); //1 byte for #0 character + {$ENDIF} + + GetMem(Buffer, 2*SZ+1); + try + {$IFNDEF NEXTGEN} + ZeroMemory(Buffer, 2*SZ+1); + {$ELSE} + FillChar(Buffer^, 2*SZ+1, 0); + {$ENDIF} + PQEscapeStringConn(FConnect.Handle, Buffer, AVal, SZ, Err); + if Err > 0 then + FConnect.CheckResult; + Result := '''' + FConnect.RawToString(Buffer) + ''''; + finally + FreeMem(Buffer); + end; + end; +end; + +function TNativeDataSet.MemoValue(P : Pointer; NeedQuote: boolean = True):String; +var + Buffer : PAnsiDACChar; + SZ : Integer; +begin + Result := ''; + if TBlobItem(P^).Blob <> nil then + begin + if TBlobItem(P^).Blob.Size = 0 then exit; + SZ := TBlobItem(P^).Blob.Size + SizeOf(Char); //null termination + GetMem(Buffer, SZ); + {$IFNDEF NEXTGEN} + ZeroMemory(Buffer,SZ); + {$ELSE} + FillChar(Buffer^,SZ, 0); + {$ENDIF} + TBlobItem(P^).Blob.Seek(Longint(0), 0); + TBlobItem(P^).Blob.Read(Buffer^, SZ); + Result := StrValue(Buffer, NeedQuote); + FreeMem(Buffer, SZ); + end; +end; + +function TNativeDataSet.BlobValue(P : Pointer; Fld: TPSQLField; NeedEscape: boolean = True): string; +begin + Result := BlobValue(TBlobItem(P^).Blob, Fld.NativeBLOBType = nbtBytea, NeedEscape); +end; + +function TNativeDataset.BlobValue(MS: TStream; isBytea: boolean; NeedEscape: Boolean = True): string; +var + Buffer, PEsc : PAnsiDACChar; + SZ : Integer; + Res : LongInt; + Off, BlSZ: Integer; +begin + Result := '0'; + if not Assigned(MS) then Exit; + + if MS.Size = 0 then + begin + if isBytea then + if NeedEscape then Result := '''''' else Result := '' + else + begin + FConnect.BeginBLOBTran; + try + FBlobHandle := lo_creat(FConnect.Handle, INV_WRITE or INV_READ); + if FBLobHandle = 0 then + raise EPSQLException.CreateMsg(FConnect,'Can''t create BLOB! lo_creat operation failed!'); + Result := UIntToStr(FBlobHandle); + finally + FConnect.CommitBLOBTran; + end; + end; + Exit; + end; + + SZ := MS.Size; + GetMem(Buffer, SZ + 1); + {$IFNDEF NEXTGEN} + ZeroMemory(Buffer, SZ + 1); + {$ELSE} + FillChar(Buffer^, SZ + 1, 0); + {$ENDIF} + MS.Seek(Longint(0), 0); + MS.Read(Buffer^, SZ); + if isBytea then + begin + if NeedEscape then + begin + PEsc := PQEscapeByteaConn(FConnect.Handle, Buffer, SZ, BlSZ); + try + Result := '''' + FConnect.RawToString(PEsc) + ''''; + finally + PQFreeMem(PEsc); + end; + end + else + Result := string(Buffer); + FreeMem(Buffer, SZ+1); + end + else //nbtOID in other case + begin + FConnect.BeginBLOBTran; + FBlobHandle := lo_creat(FConnect.Handle,INV_WRITE or INV_READ); + if FBlobHandle = 0 then + begin + FConnect.RollbackBLOBTran; + raise EPSQLException.CreateMsg(FConnect,'Can''t create BLOB! lo_creat operation failed!') + end; + FLocalBHandle := lo_open(FConnect.Handle, FBlobHandle, INV_WRITE); + try + Off := 0; + repeat + BlSZ := Min(MAX_BLOB_SIZE, SZ - off); + Res := lo_write(FConnect.Handle, FLocalBHandle, Buffer + off, BLSZ); + if Res < 0 then + raise EPSQLException.CreateMsg(FConnect,'BLOB operation failed!') + else + Inc(Off, Res); + until (off >= SZ); + FreeMem(Buffer, SZ+1); + except + lo_close(FConnect.Handle,FlocalBHandle); + FConnect.RollbackBLOBTran; + raise; + end; + lo_close(FConnect.Handle,FlocalBHandle); + FConnect.CommitBLOBTran; + Result := UIntToStr(FBlobHandle); + end; +end; + +function TPSQLEngine.QGetProcParams(hStmt: hDBIStmt; + Params: TParams): DBIResult; +begin + try + TNativeDataSet(hStmt).StoredProcGetParams(Params); + Result := DBIERR_NONE; + except + Result := CheckError; + end; + +end; + +procedure TNativeDataSet.StoredProcGetParams(Params: TParams); +var i,j: integer; +begin + if not Assigned(FStatement) then Exit; + for i:=0 to Params.Count-1 do + if Params[i].ParamType in [ptOutput, ptInputOutput] then + for j := 0 to FieldCount - 1 do + if Params[i].Name = FieldName(j) then + Params[i].AsString := Field(j); +end; + +function TNativeConnect.RawToString(S: PAnsiDACChar): string; +begin + if IsUnicodeUsed then +{$IFDEF DELPHI_12} + Result := UTF8ToUnicodeString(S) +{$ELSE} + Result := UTF8ToString(S) +{$ENDIF} + else + Result := string(S); +end; + +{$IFDEF DELPHI_15} +procedure ReverseBytes(P: Pointer; Count: Integer); +var + P1: PByte; + P2: PByte; + C: Byte; +begin + P1 := PByte(P); + P2 := PByte(P) + Count - 1; + while P1 < P2 do + begin + C := P1^; + P1^ := P2^; + P2^ := C; + System.inc(P1); + System.dec(P2); + end; +end; + +function TNativeConnect.BinaryToString(S: PAnsiDACChar; TypeOID: cardinal): string; +begin + case TypeOID of + FIELD_TYPE_INT4: + begin + ReverseBytes(S, SizeOf(Integer)); + Result := IntToStr(PInteger(S)^); + end; + FIELD_TYPE_INT2: Result := IntToStr(Swap(Smallint(S))); + end; +end; +{$ENDIF} + + +procedure TNativeConnect.ReloadGUC; +begin + GUCList(FGUCList); +end; + +procedure TNativeConnect.Reset; +begin + PQreset(FHandle); +end; + +function TPSQLEngine.Reset(hDb: DAChDBIDb): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDB).Reset; + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.CancelBackend(hDb: DAChDBIDb; PID: Integer): DBIResult; +begin + try + Database := hDb; + TNativeConnect(hDb).CancelBackend(PID); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +procedure TNativeConnect.CancelBackend(PID: Integer); +var + RES: PPGresult; + sql: DACAString; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + InternalConnect; + sql := DACAString(Format('SELECT pg_cancel_backend(%u)',[PID])); + RES := PQexec(Handle, {$IFNDEF NEXTGEN} + PAnsiChar(sql) + {$ELSE} + M.AsAnsi(sql).ToPointer + {$ENDIF} + ); + try + CheckResult; + finally + PQClear(RES); + end; +end; + +function TPSQLEngine.SelectStringDirect(hDb: DAChDBIDb; pszQuery: PChar; + var IsOk: boolean; var aResult: string; + aFieldNumber: integer): DBIResult; +begin + try + aResult := TNativeConnect(hDB).SelectStringDirect(pszQuery, IsOk, aFieldNumber); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SelectStringDirect(hDb: DAChDBIDb; pszQuery: PChar; + var IsOk: boolean; var aResult: string; aFieldName: string): DBIResult; +begin + try + aResult := TNativeConnect(hDB).SelectStringDirect(pszQuery, IsOk, PChar(aFieldName)); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TNativeConnect.SelectStringDirect(pszQuery: string; + var IsOk: boolean; aFieldNumber: integer): string; +var + Stmt : PPGresult; +begin + Result := ''; + InternalConnect; + + Stmt := _PQExecute(Self, pszQuery); + try + IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and + (PQnfields(Stmt) > aFieldNumber) and + (aFieldNumber >= 0) and + (PQntuples(Stmt) > 0); + if IsOK then + Result := RawToString(PQgetvalue(Stmt,0,aFieldNumber)); + {else + CheckResult;} + finally + PQClear(Stmt); + end; +end; + +function TNativeConnect.SelectStringDirect(pszQuery: string; + var IsOk: boolean; pszFieldName : string): string; +var + Stmt : PPGresult; + P: PAnsiDACChar; +begin + Result := ''; + InternalConnect; + + Stmt := _PQExecute(Self, pszQuery); + try + P := StringToRaw(pszFieldName); + IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and + (PQfnumber(Stmt, P) > -1) and + (PQntuples(Stmt) > 0); + if IsOK then + Result := RawToString(PQgetvalue(Stmt,0,PQfnumber(Stmt, P))); + + {$IFNDEF NEXTGEN} + DACAnsiStrDispose(P); + {$ENDIF} + finally + PQClear(Stmt); + end; +end; + +function TPSQLEngine.GetFieldTypeOID(hCursor: hDBICur; const FieldNum: integer): cardinal; +begin + Result := TNativeDataset(hCursor).FieldType(FieldNum); +end; + +procedure TFieldList.GetFLDDesc(PRecord: pFLDDesc); +begin + PRecord^ := TFLDDescList(Descs)[Position]; +end; + +procedure TFieldList.GetNextRecord(eLock: DBILockType; PRecord: Pointer; + pRecProps: pRECProps); +begin + if Position = Items then + raise EPSQLException.CreateBDE(DBIERR_EOF) + else + GetFLDDesc(PRecord); + Inc(Position); +end; + + +procedure TFieldList.GetRecordCount(var iRecCount: Integer); +begin + iRecCount := Items; +end; + +function TFieldList.GetWorkBufferSize: integer; +begin + Result := GetBufferSize; +end; + +procedure TFieldList.SetToBegin; +begin + inherited SetToBegin; + Position := 0; +end; + +procedure TFieldList.SetToBookmark(P: Pointer); +begin + SetToBegin; +end; + +function TPSQLEngine.GetFieldOrigin(hCursor: hDBICur; + const FieldNum: integer): string; +begin + Result := TNativeDataset(hCursor).FieldOrigin(FieldNum); +end; + +function TPSQLEngine.SelectStringsDirect(hDb: DAChDBIDb; pszQuery: PChar; + aList: TStrings; aFieldNumber: integer): DBIResult; +begin + try + TNativeConnect(hDB).SelectStringsDirect(pszQuery, aList, aFieldNumber); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TPSQLEngine.SelectStringsDirect(hDb: DAChDBIDb; pszQuery: PChar; + aList: TStrings; aFieldName: string): DBIResult; +begin + try + TNativeConnect(hDB).SelectStringsDirect(pszQuery, aList, aFieldName); + Result := DBIERR_NONE; + except + Result := CheckError; + end; +end; + +function TNativeConnect.SelectStringsDirect(pszQuery: string; + aList: TStrings; aFieldNumber: integer): string; +var + Stmt : PPGresult; + i: integer; + IsOK: boolean; +begin + Result := ''; + InternalConnect; + + Stmt := _PQExecute(Self, pszQuery); + try + IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and + (PQnfields(Stmt) > aFieldNumber) and + (PQntuples(Stmt) > 0); + if IsOK then + try + aList.BeginUpdate; + for i := 0 to PQntuples(Stmt) - 1 do + aList.Append(RawToString(PQgetvalue(Stmt, i, aFieldNumber))); + finally + aList.EndUpdate; + end + else + CheckResult; + finally + PQClear(Stmt); + end; +end; + + +function TNativeConnect.SelectStringsDirect(pszQuery: string; + aList: TStrings; pszFieldName: string): string; +var + Stmt : PPGresult; + P: PAnsiDACChar; + i, ColNum: integer; + IsOK: boolean; +begin + Result := ''; + InternalConnect; + + Stmt := _PQExecute(Self, pszQuery); + try + P := StringToRaw(pszFieldName); + ColNum := PQfnumber(Stmt, P); + IsOK := (PQresultStatus(Stmt) = PGRES_TUPLES_OK) and + (ColNum > -1) and + (PQntuples(Stmt) > 0); + if IsOK then + try + aList.BeginUpdate; + for i := 0 to PQntuples(Stmt) - 1 do + aList.Append(RawToString(PQgetvalue(Stmt, i, ColNum))); + finally + aList.EndUpdate; + end + else + CheckResult; + {$IFNDEF NEXTGEN} + DACAnsiStrDispose(P); + {$ENDIF} + finally + PQClear(Stmt); + end; +end; + + +{$IFDEF NEXTGEN} +{ TPListObject } + +constructor TPListObject.Create(Value: Integer); +begin + FValue := Value; +end; + +class operator TPListObject.Implicit(obj: TPListObject): Integer; +begin + Result := obj.FValue; +end; +{$ENDIF} + +destructor TPSQLIndex.Destroy; +begin + Finalize(FDesc); + inherited; +end; + +initialization + + {$IFDEF M_DEBUG} + OpenDebugFile; + {$ENDIF} + + {$IFDEF DELPHI_15} + PSQL_FS := TFormatSettings.Create(); + {$ELSE} + {$IFNDEF FPC} + GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, PSQL_FS); + {$ENDIF} + {$ENDIF} + PSQL_FS.DecimalSeparator := '.'; //for use inside StrToFloat + PSQL_FS.TimeSeparator := ':'; //for use inside FormatDateTime + +finalization + + {$IFDEF M_DEBUG} + CloseDebugFile; + {$ENDIF} + +end. diff --git a/PSQLBatch.pas b/Source/PSQLBatch.pas similarity index 96% rename from PSQLBatch.pas rename to Source/PSQLBatch.pas index 7663927..ae34570 100644 --- a/PSQLBatch.pas +++ b/Source/PSQLBatch.pas @@ -1,149 +1,149 @@ -unit PSQLBatch; - -interface - -{SVN revision: $Id$} - -Uses {$IFDEF FPC}LCLIntf,{$ENDIF}{$IFDEF MSWINDOWS}Windows,{$ENDIF} SysUtils, Classes, Db, - {$IFDEF DELPHI_6}Variants,{$ENDIF}{StdVCL,} PSQLDbTables, PSQLTypes; - -type - {TPSQLBatchExecute} - TpsqlBatchAction = (baFail, baAbort, baIgnore, baContinue); - - TPSQLBatchErrorEvent = procedure(Sender: TObject; E: EPSQLDatabaseError; SQLText : String; StatementNo : Integer) of object; - - TPSQLBatchExecute = class(TComponent) - private - FAbout : TPSQLDACAbout; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; - FAffectedRows: LongInt; - FSql: TStringList; - FDelimiter : Char; - FAction : TPSQLBatchAction; - FBeforeExecute: TNotifyEvent; - FAfterExecute: TNotifyEvent; - FOnBatchError: TPSQLBatchErrorEvent; - procedure SetSql(Value: TStringList); - protected - procedure Notification(AComponent: TComponent; Operation: TOperation); override; - function BatchExecSql(Sql: String): LongInt; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - procedure ExecSQL; - property RowsAffected: LongInt read FAffectedRows; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property Action : TPSQLBatchAction read FAction write FAction default baFail; - property Database: TPSQLDatabase read FDatabase write FDatabase; - property SQL: TStringList read FSql write SetSql; - property Delimiter : Char read FDelimiter write FDelimiter default ';'; - property OnBeforeExecute: TNotifyEvent read FBeforeExecute write FBeforeExecute; - property OnAfterExecute: TNotifyEvent read FAfterExecute write FAfterExecute; - property OnBatchError: TPSQLBatchErrorEvent read FOnBatchError write FOnBatchError; - end; - -implementation - -{TPSQLBatchExecute} -constructor TPSQLBatchExecute.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FSql := TStringList.Create; - FDelimiter := ';'; - FAction := baFail; -end; - -destructor TPSQLBatchExecute.Destroy; -begin - FSql.Free; - inherited Destroy; -end; - -procedure TPSQLBatchExecute.SetSql(Value: TStringList); -begin - FSql.Assign(Value); -end; - -procedure TPSQLBatchExecute.Notification(AComponent: TComponent; Operation: TOperation); -begin - inherited Notification(AComponent, Operation); - if (AComponent = FDatabase) and (Operation = opRemove) then - FDatabase := nil; -end; - -function TPSQLBatchExecute.BatchExecSql(Sql: String): LongInt; -var - Buffer, Token, Text: {$IFDEF FPC}ansistring{$ELSE}string{$ENDIF}; - StmtNo : Integer; -begin - Buffer := Sql; - Text := ''; - Result := 0; - StmtNo := 0; - while Buffer <> '' do - begin - if (Pos(Buffer[1], ' '#9#10#13) <> 0) and (Text <> '') then - Text := Text + ' '; - GetToken(Buffer, Token); - if (Token = FDelimiter) and (Text <> '') then - begin - try - Inc(StmtNo); - Text := Trim(Text); - Result := Result + FDatabase.Execute(Text); - Text := ''; - except - on E: EPSQLDatabaseError do - begin - if E.Message <> '' then E.Message := E.Message + '. '; - if Assigned(FOnBatchError) then - FOnBatchError(Self, E,Text,StmtNo); - case Action of - baFail: raise; - baAbort: SysUtils.Abort; - baContinue: if Assigned(FDatabase.OnException) then FDatabase.OnException(Self, E); - baIgnore: ; - end; - Text :=''; - end; - end; - end else - Text := Text + Token; - end; - Text := Trim(Text); - if Text <> '' then - try - Inc(StmtNo); - Result := Result + FDatabase.Execute(Text); - except - on E: EPSQLDatabaseError do - begin - if E.Message <> '' then E.Message := E.Message + '. '; - if Assigned(FOnBatchError) then - FOnBatchError(Self, E,Text,StmtNo); - case Action of - baFail: raise; - baAbort: SysUtils.Abort; - baContinue: if Assigned(FDatabase.OnException) then FDatabase.OnException(Self, E); - baIgnore: ; - end; - end; - end; -end; - -procedure TPSQLBatchExecute.ExecSql; -begin - if Assigned(FDatabase) then - begin - if Assigned(FBeforeExecute) then FBeforeExecute(Self); - FDatabase.Connected := True; - FAffectedRows := BatchExecSql(FSql.Text); - if Assigned(FAfterExecute) then FAfterExecute(Self); - end else - DatabaseError('Property Database not set!'); -end; - - -end. +unit PSQLBatch; + +interface + +{SVN revision: $Id$} + +Uses {$IFDEF FPC}LCLIntf,{$ENDIF}{$IFDEF MSWINDOWS}Windows,{$ENDIF} SysUtils, Classes, Db, + {$IFDEF DELPHI_6}Variants,{$ENDIF}{StdVCL,} PSQLDbTables, PSQLTypes; + +type + {TPSQLBatchExecute} + TpsqlBatchAction = (baFail, baAbort, baIgnore, baContinue); + + TPSQLBatchErrorEvent = procedure(Sender: TObject; E: EPSQLDatabaseError; SQLText : String; StatementNo : Integer) of object; + + TPSQLBatchExecute = class(TComponent) + private + FAbout : TPSQLDACAbout; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; + FAffectedRows: LongInt; + FSql: TStringList; + FDelimiter : Char; + FAction : TPSQLBatchAction; + FBeforeExecute: TNotifyEvent; + FAfterExecute: TNotifyEvent; + FOnBatchError: TPSQLBatchErrorEvent; + procedure SetSql(Value: TStringList); + protected + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + function BatchExecSql(Sql: String): LongInt; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure ExecSQL; + property RowsAffected: LongInt read FAffectedRows; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property Action : TPSQLBatchAction read FAction write FAction default baFail; + property Database: TPSQLDatabase read FDatabase write FDatabase; + property SQL: TStringList read FSql write SetSql; + property Delimiter : Char read FDelimiter write FDelimiter default ';'; + property OnBeforeExecute: TNotifyEvent read FBeforeExecute write FBeforeExecute; + property OnAfterExecute: TNotifyEvent read FAfterExecute write FAfterExecute; + property OnBatchError: TPSQLBatchErrorEvent read FOnBatchError write FOnBatchError; + end; + +implementation + +{TPSQLBatchExecute} +constructor TPSQLBatchExecute.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FSql := TStringList.Create; + FDelimiter := ';'; + FAction := baFail; +end; + +destructor TPSQLBatchExecute.Destroy; +begin + FSql.Free; + inherited Destroy; +end; + +procedure TPSQLBatchExecute.SetSql(Value: TStringList); +begin + FSql.Assign(Value); +end; + +procedure TPSQLBatchExecute.Notification(AComponent: TComponent; Operation: TOperation); +begin + inherited Notification(AComponent, Operation); + if (AComponent = FDatabase) and (Operation = opRemove) then + FDatabase := nil; +end; + +function TPSQLBatchExecute.BatchExecSql(Sql: String): LongInt; +var + Buffer, Token, Text: {$IFDEF FPC}ansistring{$ELSE}string{$ENDIF}; + StmtNo : Integer; +begin + Buffer := Sql; + Text := ''; + Result := 0; + StmtNo := 0; + while Buffer <> '' do + begin + if (Pos(Buffer[1], ' '#9#10#13) <> 0) and (Text <> '') then + Text := Text + ' '; + GetToken(Buffer, Token); + if (Token = FDelimiter) and (Text <> '') then + begin + try + Inc(StmtNo); + Text := Trim(Text); + Result := Result + FDatabase.Execute(Text); + Text := ''; + except + on E: EPSQLDatabaseError do + begin + if E.Message <> '' then E.Message := E.Message + '. '; + if Assigned(FOnBatchError) then + FOnBatchError(Self, E,Text,StmtNo); + case Action of + baFail: raise; + baAbort: SysUtils.Abort; + baContinue: if Assigned(FDatabase.OnException) then FDatabase.OnException(Self, E); + baIgnore: ; + end; + Text :=''; + end; + end; + end else + Text := Text + Token; + end; + Text := Trim(Text); + if Text <> '' then + try + Inc(StmtNo); + Result := Result + FDatabase.Execute(Text); + except + on E: EPSQLDatabaseError do + begin + if E.Message <> '' then E.Message := E.Message + '. '; + if Assigned(FOnBatchError) then + FOnBatchError(Self, E,Text,StmtNo); + case Action of + baFail: raise; + baAbort: SysUtils.Abort; + baContinue: if Assigned(FDatabase.OnException) then FDatabase.OnException(Self, E); + baIgnore: ; + end; + end; + end; +end; + +procedure TPSQLBatchExecute.ExecSql; +begin + if Assigned(FDatabase) then + begin + if Assigned(FBeforeExecute) then FBeforeExecute(Self); + FDatabase.Connected := True; + FAffectedRows := BatchExecSql(FSql.Text); + if Assigned(FAfterExecute) then FAfterExecute(Self); + end else + DatabaseError('Property Database not set!'); +end; + + +end. diff --git a/PSQLCOMP.pas b/Source/PSQLCOMP.pas similarity index 96% rename from PSQLCOMP.pas rename to Source/PSQLCOMP.pas index 0a1d385..e222def 100644 --- a/PSQLCOMP.pas +++ b/Source/PSQLCOMP.pas @@ -1,750 +1,750 @@ -{$I PSQLdac.inc} -unit PSQLCOMP; - -{SVN revision: $Id$} - -interface - -Uses SysUtils, Classes, Graphics, Controls, Forms, Dialogs, - {$IFNDEF FPC} - {$IFDEF DELPHI_5}DsgnIntf{$ELSE}DesignIntf, DesignEditors{$ENDIF}, - {$IFNDEF BCB}DsDesign,{$ENDIF} - ToolsAPI, PSQLMigrator, PSQLDump, Windows, - {$ELSE} - PropEdits, ComponentEditors, - {$ENDIF FPC} - Db, PSQLFldLinks, PSQLDbTables, PSQLupdsqled, PSQLBatch, PSQLMacroQuery, - PSQLMonitor, PSQLTools, PSQLCopy, PSQLDirectQuery, PSQLFields, - PSQLNotify; - -type - TAboutProperty = class(TPropertyEditor) - Public - procedure Edit; override; - function GetAttributes: TPropertyAttributes; override; - function GetValue: string; override; - end; - -{$IFNDEF FPC} - TMigrateExecutePropertyEditor = class(TPropertyEditor) - public - procedure Edit; override; - function GetAttributes: TPropertyAttributes; override; - function GetValue: string; override; - end; -{$ENDIF} - - TPSQLTableNamePropertyEditor = Class(TStringProperty) - Public - Function GetAttributes: TPropertyAttributes; Override; - Procedure GetValueList(List: TStrings); - Procedure GetValues(Proc: TGetStrProc); Override; - end; - - TPSQLStoredProcNamePropertyEditor = Class(TStringProperty) - Public - Function GetAttributes: TPropertyAttributes; Override; - Procedure GetValueList(List: TStrings); - Procedure GetValues(Proc: TGetStrProc); Override; - end; - - TPSQLIndexNamePropertyEditor = Class(TStringProperty) - Public - Function GetAttributes: TPropertyAttributes; Override; - Procedure GetValueList(List: TStrings); - Procedure GetValues(Proc: TGetStrProc); Override; - end; - - - TPSQLIndexFieldNamesPropertyEditor = Class(TStringProperty) - Public - Function GetAttributes: TPropertyAttributes; Override; - Procedure GetValueList(List: TStrings); - Procedure GetValues(Proc: TGetStrProc); Override; - end; - - TPSQLParamOidPropertyEditor = Class(TIntegerProperty) - Public - function GetAttributes: TPropertyAttributes; Override; - procedure GetValueList(List: TStrings); - procedure GetValues(Proc: TGetStrProc); Override; - procedure SetValue(const Value: string); override; - end; - - { TPSQLTableFieldLinkProperty } - TPSQLTableFieldLinkProperty = class(TPSQLFieldLinkProperty) - private - FTable: TPSQLTable; - protected - procedure GetFieldNamesForIndex(List: TStrings); override; - function GetIndexBased: Boolean; override; - function GetIndexDefs: TIndexDefs; override; - function GetIndexFieldNames: string; override; - function GetIndexName: string; override; - function GetMasterFields: string; override; - procedure SetIndexFieldNames(const Value: string); override; - procedure SetIndexName(const Value: string); override; - procedure SetMasterFields(const Value: string); override; - public - property IndexBased: Boolean read GetIndexBased; - property IndexDefs: TIndexDefs read GetIndexDefs; - property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames; - property IndexName: string read GetIndexName write SetIndexName; - property MasterFields: string read GetMasterFields write SetMasterFields; - procedure Edit; override; - end; - - TPSQLDataSourcePropertyEditor = Class(TComponentProperty) - Private - FCheckProc: TGetStrProc; - Procedure CheckComponent( const Value : string ); - Public - Procedure GetValues(Proc: TGetStrProc); override; - end; - - TPSQLDatabaseEditor = class(TComponentEditor) - public - procedure ExecuteVerb(Index: Integer); override; - function GetVerb(Index: Integer): string; override; - function GetVerbCount: Integer; override; - end; - -{$IFNDEF FPC} -{$IFNDEF BCB} - TPSQLDSDesigner = class(TDSDesigner) - public - procedure EndCreateFields; override; - end; - - TPSQLQueryEditor = class(TComponentEditor) - private - FConnection: TPSQLDatabase; - protected - function GetDSDesignerClass: TDSDesignerClass; virtual; - public - procedure GetTables(List: TStrings; SystemTables: Boolean); - procedure GetFields(const TableName: string; List: TStrings; SystemFields: Boolean); - procedure ExecuteVerb(Index: Integer); override; - function GetVerb(Index: Integer): string; override; - function GetVerbCount: Integer; override; - end; -{$ENDIF} -{$ENDIF} - - TPSQLStoredProcEditor = class(TComponentEditor) - public - procedure ExecuteVerb(Index: Integer); override; - function GetVerb(Index: Integer): string; override; - function GetVerbCount: Integer; override; - end; - - TPSQLUpdateSQLEditor = class(TComponentEditor) - public - procedure ExecuteVerb(Index: Integer); override; - function GetVerb(Index: Integer): string; override; - function GetVerbCount: Integer; override; - end; - - TPSQLDatabaseCharsetPropertyEditor = Class(TStringProperty) - Public - Function GetAttributes: TPropertyAttributes; Override; - Procedure GetValueList(List: TStrings); - Procedure GetValues(Proc: TGetStrProc); Override; - end; - - -Procedure Register; -Procedure RegisterPropertyEditors; - -implementation - -uses TypInfo, PSQLAboutFrm, PSQLConnFrm, PSQLStoredProcFrm, PSQLQueryEdit, PSQLTypes{$IFNDEF FPC}, DBCommon{$ENDIF}; - -{$R PDAC.DCR} - -function GetPropertyValue(Instance: TPersistent; const PropName: string): TPersistent; -var - PropInfo: PPropInfo; -begin - Result := nil; - PropInfo := TypInfo.GetPropInfo(Instance.ClassInfo, PropName); - if (PropInfo <> nil) and (PropInfo^.PropType^.Kind = tkClass) then - Result := TObject(GetOrdProp(Instance, PropInfo)) as TPersistent; -end; - -function GetIndexDefs(Component: TPersistent): TIndexDefs; -var - DataSet: TDataSet; -begin - DataSet := Component as TDataSet; - Result := GetPropertyValue(DataSet, 'IndexDefs') as TIndexDefs; - if Assigned(Result) then - begin - Result.Updated := False; - Result.Update; - end; -end; - -{About Property} -function TAboutProperty.GetAttributes: TPropertyAttributes; -begin - Result := [paDialog, paReadOnly] -end; - -procedure TAboutProperty.Edit; -begin - DAC4PSQLShowAbout(GetComponent(0).ClassName); //mi:2007-09-28 -end; - -function TAboutProperty.GetValue: string; -begin - Result := 'About...'; -end; - -{PSQLTableName} -Function TPSQLTableNamePropertyEditor.GetAttributes : TPropertyAttributes; -begin - Result := [paValueList, paSortList, paMultiSelect]; -end; - -Procedure TPSQLTableNamePropertyEditor.GetValues(Proc: TGetStrProc); -var - I : Integer; - Values : TStringList; -begin - Values := TStringList.Create; - Try - GetValueList(Values); - for I := 0 to Values.Count - 1 do Proc(Values[I]); - Finally - Values.Free; - end; -end; - -Procedure TPSQLTableNamePropertyEditor.GetValueList(List: TStrings); -var - DB: TPSQLDatabase; - Cursor: TCursor; -begin - if GetComponent(0) is TPSQLTable then - DB := (GetComponent(0) as TPSQLTable).Database - else - if GetComponent(0) is TPSQLCopy then - DB := (GetComponent(0) as TPSQLCopy).Database - else - DB := nil; - if Db = nil then raise EDatabaseError.Create('Database property is not set'); - Cursor := Screen.Cursor; - try - DB.GetTableNames('', False, List); - finally - Screen.Cursor := Cursor; - end; -end; - -{TPSQLIndexName} -Function TPSQLIndexNamePropertyEditor.GetAttributes : TPropertyAttributes; -begin - Result := [paValueList, paSortList, paMultiSelect]; -end; - -Procedure TPSQLIndexNamePropertyEditor.GetValues(Proc: TGetStrProc); -var - I : Integer; - Values : TStringList; -begin - Values := TStringList.Create; - Try - GetValueList( Values ); - for I := 0 to Values.Count - 1 do Proc(Values[I]); - Finally - Values.Free; - end; -end; - -Procedure TPSQLIndexNamePropertyEditor.GetValueList(List : TStrings); -begin - (GetComponent(0) as TPSQLTable).GetIndexNames(List); -end; - -{TPSQLIndexFieldNamesPropertyEditor} -Function TPSQLIndexFieldNamesPropertyEditor.GetAttributes : TPropertyAttributes; -begin - Result := [paValueList, paSortList, paMultiSelect]; -end; - -Procedure TPSQLIndexFieldNamesPropertyEditor.GetValues(Proc: TGetStrProc); -var - I : Integer; - Values : TStringList; -begin - Values := TStringList.Create; - Try - GetValueList(Values); - for I := 0 to Values.Count-1 do Proc(Values[I]); - Finally - Values.Free; - end; -end; - -Procedure TPSQLIndexFieldNamesPropertyEditor.GetValueList( List : TStrings ); -var - I: Integer; - IndexDefs: TIndexDefs; -begin - IndexDefs := GetIndexDefs(GetComponent(0)); - for I := 0 to IndexDefs.Count - 1 do - with IndexDefs[I] do - if (Options * [ixExpression, ixDescending] = []) and (Fields <> '') then List.Add(Fields); -end; - -{ TPSQLTableFieldLinkProperty } -procedure TPSQLTableFieldLinkProperty.Edit; -var - Table: TPSQLTable; -begin - Table := DataSet as TPSQLTable; - {$WARNINGS OFF} //make D5 happy - FTable := TPSQLTable.Create(nil); - {$WARNINGS ON} //make D5 happy - try - FTable.Database := Table.Database; - FTable.TableName := Table.TableName; - if Table.IndexFieldNames <> '' then - FTable.IndexFieldNames := Table.IndexFieldNames else - FTable.IndexName := Table.IndexName; - FTable.MasterFields := Table.MasterFields; - FTable.Open; - inherited Edit; - if Changed then - begin - Table.MasterFields := FTable.MasterFields; - if FTable.IndexFieldNames <> '' then - Table.IndexFieldNames := FTable.IndexFieldNames else - Table.IndexName := FTable.IndexName; - end; - finally - FTable.Free; - end; -end; - -procedure TPSQLTableFieldLinkProperty.GetFieldNamesForIndex(List: TStrings); -var - i: Integer; -begin - for i := 0 to FTable.IndexFieldCount - 1 do - List.Add(FTable.IndexFields[i].FieldName); -end; - -function TPSQLTableFieldLinkProperty.GetIndexBased: Boolean; -begin - Result := {$IFDEF DELPHI_4}False{$ELSE}True{$ENDIF}; -end; - -function TPSQLTableFieldLinkProperty.GetIndexDefs: TIndexDefs; -begin - Result := FTable.IndexDefs; -end; - -function TPSQLTableFieldLinkProperty.GetIndexFieldNames: string; -begin - Result := FTable.IndexFieldNames; -end; - -function TPSQLTableFieldLinkProperty.GetIndexName: string; -begin - Result := FTable.IndexName; -end; - -function TPSQLTableFieldLinkProperty.GetMasterFields: string; -begin - Result := FTable.MasterFields; -end; - -procedure TPSQLTableFieldLinkProperty.SetIndexFieldNames(const Value: string); -begin - FTable.IndexFieldNames := Value; -end; - -procedure TPSQLTableFieldLinkProperty.SetIndexName(const Value: string); -begin - if Value = 'Primary' then - FTable.IndexName := '' else - FTable.IndexName := Value; -end; - -procedure TPSQLTableFieldLinkProperty.SetMasterFields(const Value: string); -begin - FTable.MasterFields := Value; -end; - -{PSQLDataSource} -Procedure TPSQLDataSourcePropertyEditor.CheckComponent( const Value : string ); -var - J: Integer; - DataSource: TDataSource; -begin - {$IFDEF FPC} - DataSource := TDataSource(PropertyHook.GetComponent(Value)); - {$ELSE} - DataSource := TDataSource(Designer.GetComponent(Value )); - {$ENDIF} - for J := 0 to Pred( PropCount ) do - if TDataSet( GetComponent( J ) ).IsLinkedTo( DataSource ) then - Exit; - FCheckProc( Value ); -end; - -Procedure TPSQLDataSourcePropertyEditor.GetValues( Proc : TGetStrProc ); -begin - FCheckProc := Proc; - Inherited GetValues( CheckComponent ); -end; - -{PSQLDatabase Editor} -procedure TPSQLDatabaseEditor.ExecuteVerb(Index: Integer); -begin - case Index of - 0: EditDatabase(TPSQLDatabase(Component)); - 1: TPSQLDatabase(Component).Connected := not TPSQLDatabase(Component).Connected; - end; - Designer.Modified; -end; - -function TPSQLDatabaseEditor.GetVerb(Index: Integer): string; -begin - case Index of - 0: Result := 'PSQLDatabase &Editor...'; - 1: if TPSQLDatabase(Component).Connected then Result := '&Disconnect from server' else Result := '&Connect to server'; - end; -end; - -function TPSQLDatabaseEditor.GetVerbCount: Integer; -begin - Result := 2; -end; - -procedure TPSQLUpdateSQLEditor.ExecuteVerb(Index: Integer); -begin - if EditPSQLUpdateSQL(TPSQLUpdateSQL(Component)) then Designer.Modified; -end; - -function TPSQLUpdateSQLEditor.GetVerb(Index: Integer): string; -begin - Result := '&PSQLUpdateSQL Editor...'; -end; - -function TPSQLUpdateSQLEditor.GetVerbCount: Integer; -begin - Result := 1; -end; - - -Procedure RegisterPropertyEditors; -begin - RegisterPropertyEditor(TypeInfo(string), TPSQLDatabase, 'CharSet', TPSQLDatabaseCharsetPropertyEditor); - RegisterPropertyEditor(TypeInfo(string), TPSQLCopy, 'Encoding', TPSQLDatabaseCharsetPropertyEditor); - RegisterPropertyEditor(TypeInfo(TFileName), TPSQLTable, 'TableName', TPSQLTableNamePropertyEditor); - RegisterPropertyEditor(TypeInfo(cardinal), TPSQLParam, 'DataTypeOID', TPSQLParamOidPropertyEditor); - RegisterPropertyEditor(TypeInfo(TFileName), TPSQLCopy, 'TableName', TPSQLTableNamePropertyEditor); - RegisterPropertyEditor(TypeInfo(string), TPSQLTable, 'IndexName', TPSQLIndexNamePropertyEditor); - RegisterPropertyEditor(TypeInfo(string), TPSQLStoredProc, 'StoredProcName', TPSQLStoredProcNamePropertyEditor); - RegisterPropertyEditor(TypeInfo(string), TPSQLTable, 'IndexFieldNames', TPSQLIndexFieldNamesPropertyEditor); - RegisterPropertyEditor(TypeInfo(TDataSource), TPSQLTable, 'MasterSource', TPSQLDataSourcePropertyEditor); - RegisterPropertyEditor(TypeInfo(string), TPSQLTable, 'MasterFields', TPSQLTableFieldLinkProperty); - RegisterPropertyEditor(TypeInfo(TPSQLDACAbout), nil, '', TAboutProperty); - {$IFNDEF FPC} - RegisterPropertyEditor(TypeInfo(Boolean), TBDE2PSQLDAC, 'Execute', TMigrateExecutePropertyEditor); - RegisterPropertyEditor(TypeInfo(string), TPSQLDump, 'Encoding', TPSQLDatabaseCharsetPropertyEditor); - {$ENDIF} -end; - -{$IFDEF FPC} -procedure RegisterFields(const FieldClasses: array of TPersistentClass); -begin - RegisterClasses(FieldClasses); -end; -{$ENDIF} - -procedure Register; -begin - {$IFDEF DELPHI_9} - if Assigned(SplashScreenServices) then - begin - ForceDemandLoadState(dlDisable); - SplashScreenServices.AddPluginBitmap(Format('PostgresDAC Component Suite %s', [PSQLDBTables.VERSION]), - LoadBitmap(FindResourceHInstance(HInstance), 'PSQLLOGO'), - False, - 'MIT License'); - end; - {$ENDIF} - - RegisterComponents('PostgresDAC', - [TPSQLDatabase, TPSQLTable, TPSQLQuery, TPSQLStoredProc, TPSQLUpdateSQL, TPSQLNotify, - TPSQLBatchExecute, TPSQLMacroQuery, TPSQLMonitor, TPSQLDirectQuery, - TPSQLTools, TPSQLCopy {$IFNDEF FPC},TPSQLDump, TPSQLRestore, TBDE2PSQLDAC{$ENDIF}] ); - RegisterComponentEditor(TPSQLDatabase, TPSQLDatabaseEditor); - {$IFNDEF FPC}{$IFNDEF BCB}RegisterComponentEditor(TPSQLQuery, TPSQLQueryEditor);{$ENDIF}{$ENDIF} - RegisterComponentEditor(TPSQLUpdateSQL,TPSQLUpdateSQLEditor); - RegisterComponentEditor(TPSQLStoredProc,TPSQLStoredProcEditor); - RegisterFields([TPSQLGuidField - {$IFDEF DELPHI_12} - ,TPSQLPointField, TPSQLCircleField, TPSQLBoxField, TPSQLLSegField, TPSQLRangeField - {$ENDIF DELPHI_12} - ]); - - RegisterPropertyEditors; -end; - -{ TMigrateExecutePropertyEditor } -{$IFNDEF FPC} -procedure TMigrateExecutePropertyEditor.Edit; -begin - TBDE2PSQLDAC(GetComponent(0)).Migrate; - Modified; // PaGo 23.05.2007 -end; - -function TMigrateExecutePropertyEditor.GetAttributes: TPropertyAttributes; -begin - Result := [paDialog, paReadOnly]; -end; - -function TMigrateExecutePropertyEditor.GetValue: string; -begin - Result := 'Press to Migrate...'; -end; -{$ENDIF} -{ TPSQLStoredProcNamePropertyEditor } - -function TPSQLStoredProcNamePropertyEditor.GetAttributes: TPropertyAttributes; -begin - Result := [paValueList, paSortList, paMultiSelect]; -end; - -procedure TPSQLStoredProcNamePropertyEditor.GetValueList(List: TStrings); -var - Proc: TPSQLStoredProc; -begin - Proc := GetComponent(0) as TPSQLStoredProc; - if Proc.Database = nil then raise EDatabaseError.Create('Database property is not set'); - Proc.Database.GetStoredProcNames('', List); -end; - - -procedure TPSQLStoredProcNamePropertyEditor.GetValues(Proc: TGetStrProc); -var - I : Integer; - Values : TStringList; -begin - Values := TStringList.Create; - Try - GetValueList(Values); - for I := 0 to Values.Count - 1 do Proc(Values[I]); - Finally - Values.Free; - end; -end; -{ TPSQLDatabaseCharsetPropertyEditor } - -function TPSQLDatabaseCharsetPropertyEditor.GetAttributes: TPropertyAttributes; -begin - Result := [paValueList, paSortList, paMultiSelect]; -end; - -procedure TPSQLDatabaseCharsetPropertyEditor.GetValueList(List: TStrings); -var - DB: TPSQLDatabase; - AComp: TPersistent; -begin - DB := nil; - AComp := GetComponent(0); - if AComp is TPSQLDatabase then - DB := AComp as TPSQLDatabase -{$IFNDEF FPC} - else - if (AComp is TPSQLDump) and Assigned((AComp as TPSQLDump).Database) then - DB := (AComp as TPSQLDump).Database -{$ENDIF} - else - if (AComp is TAbstractCopyObject) and Assigned((AComp as TAbstractCopyObject).Database) then - DB := (AComp as TAbstractCopyObject).Database; - if not Assigned(DB) or not DB.Connected then - List.CommaText := 'BIG5,EUC_CN,EUC_JIS_2004,EUC_JP,EUC_KR,EUC_TW,'+ - 'GB18030,GBK,ISO_8859_5,ISO_8859_6,ISO_8859_7,ISO_8859_8,JOHAB,KOI8,LATIN1,'+ - 'LATIN10,LATIN2,LATIN3,LATIN4,LATIN5,LATIN6,LATIN7,LATIN8,LATIN9,MULE_INTERNAL,'+ - 'SHIFT_JIS_2004,SJIS,SQL_ASCII,UHC,UTF8,WIN1250,WIN1251,WIN1252,WIN1253,'+ - 'WIN1254,WIN1255,WIN1256,WIN1257,WIN1258,WIN866,WIN874' - else - DB.GetCharsets(List); -end; - -procedure TPSQLDatabaseCharsetPropertyEditor.GetValues(Proc: TGetStrProc); -var - I : Integer; - Values : TStringList; -begin - Values := TStringList.Create; - Try - GetValueList(Values); - for I := 0 to Values.Count - 1 do Proc(Values[I]); - Finally - Values.Free; - end; -end; - -{ TPSQLStoredProcEditor } - -procedure TPSQLStoredProcEditor.ExecuteVerb(Index: Integer); -begin - case Index of - 0: if EditStoredProc(TPSQLStoredProc(Component)) then Designer.Modified; - end; -end; - -function TPSQLStoredProcEditor.GetVerb(Index: Integer): string; -begin - case Index of - 0: Result := 'TPSQLStoredProc Editor...'; - end; -end; - -function TPSQLStoredProcEditor.GetVerbCount: Integer; -begin - Result := 1; -end; - -{ TPSQLDataSetEditor } -{$IFNDEF FPC} -{$IFNDEF BCB} -procedure TPSQLQueryEditor.ExecuteVerb(Index: Integer); -var - SQL: string; - TableName: string; -begin - case Index of - 0: ShowFieldsEditor(Designer, TDataSet(Component), GetDSDesignerClass); - 1: - begin - FConnection := TPSQLQuery(Component).Database; - try - SQL := TPSQLQuery(Component).SQL.Text; - if SQL <> '' then - TableName := GetTableNameFromSQL(SQL); - if EditSQL(SQL, GetTables, GetFields, TableName) then - TPSQLQuery(Component).SQL.Text := SQL; - finally - FConnection := nil; - end; - end; - 2: TPSQLQuery(Component).Active := not TPSQLQuery(Component).Active; - end; - Designer.Modified; -end; - -function TPSQLQueryEditor.GetDSDesignerClass: TDSDesignerClass; -begin - Result := TPSQLDSDesigner; -end; - -procedure TPSQLQueryEditor.GetFields(const TableName: string; - List: TStrings; SystemFields: Boolean); -var S: string; -begin - List.Clear; - S := Format('SELECT quote_ident(attname) FROM pg_attribute WHERE attrelid = %s::regclass', - [QuotedStr(Tablename)]); - if not SystemFields then - S := S + ' AND attnum > 0'; - if Assigned(FConnection) and FConnection.Connected then - FConnection.SelectStrings(S, List); -end; - -procedure TPSQLQueryEditor.GetTables(List: TStrings; - SystemTables: Boolean); -begin - List.Clear; - if Assigned(FConnection) and FConnection.Connected then - FConnection.GetTableNames('', SystemTables, List); -end; - -function TPSQLQueryEditor.GetVerb(Index: Integer): string; -begin - case Index of - 0: Result := 'Fields Editor...'; - 1: Result := 'SQL Editor...'; - 2: if TPSQLQuery(Component).Active then Result := 'Close' else Result := 'Open'; - end; -end; - -function TPSQLQueryEditor.GetVerbCount: Integer; -begin - Result := 3; -end; - -{ TPSQLDSDesigner } - -procedure TPSQLDSDesigner.EndCreateFields; -var OldState: boolean; -begin - inherited; - if not (DataSet is TPSQLQuery) then Exit; - if not (dsoPopulateFieldsOrigin in TPSQLQuery(DataSet).Options) then Exit; - - OldState := TPSQLQuery(DataSet).Active; - try - TPSQLQuery(DataSet).Active := True; - TPSQLQuery(DataSet).PopulateFieldsOrigin(); - finally - TPSQLQuery(DataSet).Active := OldState; - end; -end; -{$ENDIF} -{$ENDIF} - -{ TPSQLParamOidPropertyEditor } -function TPSQLParamOidPropertyEditor.GetAttributes: TPropertyAttributes; -begin - Result := [paValueList, paMultiSelect]; -end; - -procedure TPSQLParamOidPropertyEditor.GetValueList(List: TStrings); -begin - List.Text := - '"char"=18'#13'abstime=702'#13'aclitem=1033'#13'bit varying=1562'#13'bit=1560'#13'bool=16'#13'box=603'#13'bytea=17'#13'char=1042'#13''+ - 'cid=29'#13'cidr=650'#13'circle=718'#13'date=1082'#13'float4=700'#13'float8=701'#13'inet=869'#13'int2=21'#13'int2vector=22'#13'int4=23'#13'int8=20'#13''+ - 'interval=1186'#13'line=628'#13'lseg=601'#13'macaddr=829'#13'money=790'#13'name=19'#13'numeric=1700'#13'oid=26'#13'oidvector=30'#13'path=602'#13''+ - 'point=600'#13'polygon=604'#13'refcursor=1790'#13'regclass=2205'#13'regoper=2203'#13'regoperator=2204'#13'regproc=24'#13'regprocedure=2202'#13''+ - 'regtype=2206'#13'reltime=703'#13'smgr=210'#13'text=25'#13'tid=27'#13'time with time zone=1266'#13'time=1083'#13'timestamp with time zone=1184'#13''+ - 'timestamp=1114'#13'tinterval=704'#13'tsquery=3615'#13'tsvector=3614'#13'uuid=2950'#13'varchar=1043'#13'xid=28'#13'xml=142'#13''+ - //arrays bellow - 'abstime[]=1023'#13'aclitem[]=1034'#13'bit varying[]=1563'#13'bit[]=1561'#13'bool[]=1000'#13'box[]=1020'#13'bpchar[]=1014'#13'bytea[]=1001'#13''+ - 'char[]=1002'#13'cid[]=1012'#13'cidr[]=651'#13'circle[]=719'#13'date[]=1182'#13'float4[]=1021'#13'float8[]=1022'#13'inet[]=1041'#13'int2[]=1005'#13''+ - 'int2vector[]=1006'#13'int4[]=1007'#13'int8[]=1016'#13'interval[]=1187'#13'line[]=629'#13'lseg[]=1018'#13'macaddr[]=1040'#13'money[]=791'#13''+ - 'name[]=1003'#13'numeric[]=1231'#13'oid[]=1028'#13'oidvector[]=1013'#13'path[]=1019'#13'point[]=1017'#13'polygon[]=1027'#13'refcursor[]=2201'#13''+ - 'regclass[]=2210'#13'regoper[]=2208'#13'regoperator[]=2209'#13'regproc[]=1008'#13'regprocedure[]=2207'#13'regtype[]=2211'#13'reltime[]=1024'#13''+ - 'text[]=1009'#13'tid[]=1010'#13'time with time zone[]=1270'#13'time[]=1183'#13'timestamp with time zone[]=1185'#13'timestamp[]=1115'#13''+ - 'tinterval[]=1025'#13'varchar[]=1015'#13'xid[]=1011'; -end; - -procedure TPSQLParamOidPropertyEditor.GetValues(Proc: TGetStrProc); -var - I : Integer; - Values : TStringList; -begin - Values := TStringList.Create; - Try - GetValueList(Values); - for I := 0 to Values.Count-1 do Proc(Values[I]); - Finally - Values.Free; - end; -end; - -procedure TPSQLParamOidPropertyEditor.SetValue(const Value: string); -begin - inherited SetValue(Copy(Value, Pos('=', Value) + 1, MaxInt)); -end; - -initialization - -end. +{$I PSQLdac.inc} +unit PSQLCOMP; + +{SVN revision: $Id$} + +interface + +Uses SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + {$IFNDEF FPC} + {$IFDEF DELPHI_5}DsgnIntf{$ELSE}DesignIntf, DesignEditors{$ENDIF}, + {$IFNDEF BCB}DsDesign,{$ENDIF} + ToolsAPI, PSQLMigrator, PSQLDump, Windows, + {$ELSE} + PropEdits, ComponentEditors, + {$ENDIF FPC} + Db, PSQLFldLinks, PSQLDbTables, PSQLupdsqled, PSQLBatch, PSQLMacroQuery, + PSQLMonitor, PSQLTools, PSQLCopy, PSQLDirectQuery, PSQLFields, + PSQLNotify; + +type + TAboutProperty = class(TPropertyEditor) + Public + procedure Edit; override; + function GetAttributes: TPropertyAttributes; override; + function GetValue: string; override; + end; + +{$IFNDEF FPC} + TMigrateExecutePropertyEditor = class(TPropertyEditor) + public + procedure Edit; override; + function GetAttributes: TPropertyAttributes; override; + function GetValue: string; override; + end; +{$ENDIF} + + TPSQLTableNamePropertyEditor = Class(TStringProperty) + Public + Function GetAttributes: TPropertyAttributes; Override; + Procedure GetValueList(List: TStrings); + Procedure GetValues(Proc: TGetStrProc); Override; + end; + + TPSQLStoredProcNamePropertyEditor = Class(TStringProperty) + Public + Function GetAttributes: TPropertyAttributes; Override; + Procedure GetValueList(List: TStrings); + Procedure GetValues(Proc: TGetStrProc); Override; + end; + + TPSQLIndexNamePropertyEditor = Class(TStringProperty) + Public + Function GetAttributes: TPropertyAttributes; Override; + Procedure GetValueList(List: TStrings); + Procedure GetValues(Proc: TGetStrProc); Override; + end; + + + TPSQLIndexFieldNamesPropertyEditor = Class(TStringProperty) + Public + Function GetAttributes: TPropertyAttributes; Override; + Procedure GetValueList(List: TStrings); + Procedure GetValues(Proc: TGetStrProc); Override; + end; + + TPSQLParamOidPropertyEditor = Class(TIntegerProperty) + Public + function GetAttributes: TPropertyAttributes; Override; + procedure GetValueList(List: TStrings); + procedure GetValues(Proc: TGetStrProc); Override; + procedure SetValue(const Value: string); override; + end; + + { TPSQLTableFieldLinkProperty } + TPSQLTableFieldLinkProperty = class(TPSQLFieldLinkProperty) + private + FTable: TPSQLTable; + protected + procedure GetFieldNamesForIndex(List: TStrings); override; + function GetIndexBased: Boolean; override; + function GetIndexDefs: TIndexDefs; override; + function GetIndexFieldNames: string; override; + function GetIndexName: string; override; + function GetMasterFields: string; override; + procedure SetIndexFieldNames(const Value: string); override; + procedure SetIndexName(const Value: string); override; + procedure SetMasterFields(const Value: string); override; + public + property IndexBased: Boolean read GetIndexBased; + property IndexDefs: TIndexDefs read GetIndexDefs; + property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames; + property IndexName: string read GetIndexName write SetIndexName; + property MasterFields: string read GetMasterFields write SetMasterFields; + procedure Edit; override; + end; + + TPSQLDataSourcePropertyEditor = Class(TComponentProperty) + Private + FCheckProc: TGetStrProc; + Procedure CheckComponent( const Value : string ); + Public + Procedure GetValues(Proc: TGetStrProc); override; + end; + + TPSQLDatabaseEditor = class(TComponentEditor) + public + procedure ExecuteVerb(Index: Integer); override; + function GetVerb(Index: Integer): string; override; + function GetVerbCount: Integer; override; + end; + +{$IFNDEF FPC} +{$IFNDEF BCB} + TPSQLDSDesigner = class(TDSDesigner) + public + procedure EndCreateFields; override; + end; + + TPSQLQueryEditor = class(TComponentEditor) + private + FConnection: TPSQLDatabase; + protected + function GetDSDesignerClass: TDSDesignerClass; virtual; + public + procedure GetTables(List: TStrings; SystemTables: Boolean); + procedure GetFields(const TableName: string; List: TStrings; SystemFields: Boolean); + procedure ExecuteVerb(Index: Integer); override; + function GetVerb(Index: Integer): string; override; + function GetVerbCount: Integer; override; + end; +{$ENDIF} +{$ENDIF} + + TPSQLStoredProcEditor = class(TComponentEditor) + public + procedure ExecuteVerb(Index: Integer); override; + function GetVerb(Index: Integer): string; override; + function GetVerbCount: Integer; override; + end; + + TPSQLUpdateSQLEditor = class(TComponentEditor) + public + procedure ExecuteVerb(Index: Integer); override; + function GetVerb(Index: Integer): string; override; + function GetVerbCount: Integer; override; + end; + + TPSQLDatabaseCharsetPropertyEditor = Class(TStringProperty) + Public + Function GetAttributes: TPropertyAttributes; Override; + Procedure GetValueList(List: TStrings); + Procedure GetValues(Proc: TGetStrProc); Override; + end; + + +Procedure Register; +Procedure RegisterPropertyEditors; + +implementation + +uses TypInfo, PSQLAboutFrm, PSQLConnFrm, PSQLStoredProcFrm, PSQLQueryEdit, PSQLTypes{$IFNDEF FPC}, DBCommon{$ENDIF}; + +{$R PDAC.DCR} + +function GetPropertyValue(Instance: TPersistent; const PropName: string): TPersistent; +var + PropInfo: PPropInfo; +begin + Result := nil; + PropInfo := TypInfo.GetPropInfo(Instance.ClassInfo, PropName); + if (PropInfo <> nil) and (PropInfo^.PropType^.Kind = tkClass) then + Result := TObject(GetOrdProp(Instance, PropInfo)) as TPersistent; +end; + +function GetIndexDefs(Component: TPersistent): TIndexDefs; +var + DataSet: TDataSet; +begin + DataSet := Component as TDataSet; + Result := GetPropertyValue(DataSet, 'IndexDefs') as TIndexDefs; + if Assigned(Result) then + begin + Result.Updated := False; + Result.Update; + end; +end; + +{About Property} +function TAboutProperty.GetAttributes: TPropertyAttributes; +begin + Result := [paDialog, paReadOnly] +end; + +procedure TAboutProperty.Edit; +begin + DAC4PSQLShowAbout(GetComponent(0).ClassName); //mi:2007-09-28 +end; + +function TAboutProperty.GetValue: string; +begin + Result := 'About...'; +end; + +{PSQLTableName} +Function TPSQLTableNamePropertyEditor.GetAttributes : TPropertyAttributes; +begin + Result := [paValueList, paSortList, paMultiSelect]; +end; + +Procedure TPSQLTableNamePropertyEditor.GetValues(Proc: TGetStrProc); +var + I : Integer; + Values : TStringList; +begin + Values := TStringList.Create; + Try + GetValueList(Values); + for I := 0 to Values.Count - 1 do Proc(Values[I]); + Finally + Values.Free; + end; +end; + +Procedure TPSQLTableNamePropertyEditor.GetValueList(List: TStrings); +var + DB: TPSQLDatabase; + Cursor: TCursor; +begin + if GetComponent(0) is TPSQLTable then + DB := (GetComponent(0) as TPSQLTable).Database + else + if GetComponent(0) is TPSQLCopy then + DB := (GetComponent(0) as TPSQLCopy).Database + else + DB := nil; + if Db = nil then raise EDatabaseError.Create('Database property is not set'); + Cursor := Screen.Cursor; + try + DB.GetTableNames('', False, List); + finally + Screen.Cursor := Cursor; + end; +end; + +{TPSQLIndexName} +Function TPSQLIndexNamePropertyEditor.GetAttributes : TPropertyAttributes; +begin + Result := [paValueList, paSortList, paMultiSelect]; +end; + +Procedure TPSQLIndexNamePropertyEditor.GetValues(Proc: TGetStrProc); +var + I : Integer; + Values : TStringList; +begin + Values := TStringList.Create; + Try + GetValueList( Values ); + for I := 0 to Values.Count - 1 do Proc(Values[I]); + Finally + Values.Free; + end; +end; + +Procedure TPSQLIndexNamePropertyEditor.GetValueList(List : TStrings); +begin + (GetComponent(0) as TPSQLTable).GetIndexNames(List); +end; + +{TPSQLIndexFieldNamesPropertyEditor} +Function TPSQLIndexFieldNamesPropertyEditor.GetAttributes : TPropertyAttributes; +begin + Result := [paValueList, paSortList, paMultiSelect]; +end; + +Procedure TPSQLIndexFieldNamesPropertyEditor.GetValues(Proc: TGetStrProc); +var + I : Integer; + Values : TStringList; +begin + Values := TStringList.Create; + Try + GetValueList(Values); + for I := 0 to Values.Count-1 do Proc(Values[I]); + Finally + Values.Free; + end; +end; + +Procedure TPSQLIndexFieldNamesPropertyEditor.GetValueList( List : TStrings ); +var + I: Integer; + IndexDefs: TIndexDefs; +begin + IndexDefs := GetIndexDefs(GetComponent(0)); + for I := 0 to IndexDefs.Count - 1 do + with IndexDefs[I] do + if (Options * [ixExpression, ixDescending] = []) and (Fields <> '') then List.Add(Fields); +end; + +{ TPSQLTableFieldLinkProperty } +procedure TPSQLTableFieldLinkProperty.Edit; +var + Table: TPSQLTable; +begin + Table := DataSet as TPSQLTable; + {$WARNINGS OFF} //make D5 happy + FTable := TPSQLTable.Create(nil); + {$WARNINGS ON} //make D5 happy + try + FTable.Database := Table.Database; + FTable.TableName := Table.TableName; + if Table.IndexFieldNames <> '' then + FTable.IndexFieldNames := Table.IndexFieldNames else + FTable.IndexName := Table.IndexName; + FTable.MasterFields := Table.MasterFields; + FTable.Open; + inherited Edit; + if Changed then + begin + Table.MasterFields := FTable.MasterFields; + if FTable.IndexFieldNames <> '' then + Table.IndexFieldNames := FTable.IndexFieldNames else + Table.IndexName := FTable.IndexName; + end; + finally + FTable.Free; + end; +end; + +procedure TPSQLTableFieldLinkProperty.GetFieldNamesForIndex(List: TStrings); +var + i: Integer; +begin + for i := 0 to FTable.IndexFieldCount - 1 do + List.Add(FTable.IndexFields[i].FieldName); +end; + +function TPSQLTableFieldLinkProperty.GetIndexBased: Boolean; +begin + Result := {$IFDEF DELPHI_4}False{$ELSE}True{$ENDIF}; +end; + +function TPSQLTableFieldLinkProperty.GetIndexDefs: TIndexDefs; +begin + Result := FTable.IndexDefs; +end; + +function TPSQLTableFieldLinkProperty.GetIndexFieldNames: string; +begin + Result := FTable.IndexFieldNames; +end; + +function TPSQLTableFieldLinkProperty.GetIndexName: string; +begin + Result := FTable.IndexName; +end; + +function TPSQLTableFieldLinkProperty.GetMasterFields: string; +begin + Result := FTable.MasterFields; +end; + +procedure TPSQLTableFieldLinkProperty.SetIndexFieldNames(const Value: string); +begin + FTable.IndexFieldNames := Value; +end; + +procedure TPSQLTableFieldLinkProperty.SetIndexName(const Value: string); +begin + if Value = 'Primary' then + FTable.IndexName := '' else + FTable.IndexName := Value; +end; + +procedure TPSQLTableFieldLinkProperty.SetMasterFields(const Value: string); +begin + FTable.MasterFields := Value; +end; + +{PSQLDataSource} +Procedure TPSQLDataSourcePropertyEditor.CheckComponent( const Value : string ); +var + J: Integer; + DataSource: TDataSource; +begin + {$IFDEF FPC} + DataSource := TDataSource(PropertyHook.GetComponent(Value)); + {$ELSE} + DataSource := TDataSource(Designer.GetComponent(Value )); + {$ENDIF} + for J := 0 to Pred( PropCount ) do + if TDataSet( GetComponent( J ) ).IsLinkedTo( DataSource ) then + Exit; + FCheckProc( Value ); +end; + +Procedure TPSQLDataSourcePropertyEditor.GetValues( Proc : TGetStrProc ); +begin + FCheckProc := Proc; + Inherited GetValues( CheckComponent ); +end; + +{PSQLDatabase Editor} +procedure TPSQLDatabaseEditor.ExecuteVerb(Index: Integer); +begin + case Index of + 0: EditDatabase(TPSQLDatabase(Component)); + 1: TPSQLDatabase(Component).Connected := not TPSQLDatabase(Component).Connected; + end; + Designer.Modified; +end; + +function TPSQLDatabaseEditor.GetVerb(Index: Integer): string; +begin + case Index of + 0: Result := 'PSQLDatabase &Editor...'; + 1: if TPSQLDatabase(Component).Connected then Result := '&Disconnect from server' else Result := '&Connect to server'; + end; +end; + +function TPSQLDatabaseEditor.GetVerbCount: Integer; +begin + Result := 2; +end; + +procedure TPSQLUpdateSQLEditor.ExecuteVerb(Index: Integer); +begin + if EditPSQLUpdateSQL(TPSQLUpdateSQL(Component)) then Designer.Modified; +end; + +function TPSQLUpdateSQLEditor.GetVerb(Index: Integer): string; +begin + Result := '&PSQLUpdateSQL Editor...'; +end; + +function TPSQLUpdateSQLEditor.GetVerbCount: Integer; +begin + Result := 1; +end; + + +Procedure RegisterPropertyEditors; +begin + RegisterPropertyEditor(TypeInfo(string), TPSQLDatabase, 'CharSet', TPSQLDatabaseCharsetPropertyEditor); + RegisterPropertyEditor(TypeInfo(string), TPSQLCopy, 'Encoding', TPSQLDatabaseCharsetPropertyEditor); + RegisterPropertyEditor(TypeInfo(TFileName), TPSQLTable, 'TableName', TPSQLTableNamePropertyEditor); + RegisterPropertyEditor(TypeInfo(cardinal), TPSQLParam, 'DataTypeOID', TPSQLParamOidPropertyEditor); + RegisterPropertyEditor(TypeInfo(TFileName), TPSQLCopy, 'TableName', TPSQLTableNamePropertyEditor); + RegisterPropertyEditor(TypeInfo(string), TPSQLTable, 'IndexName', TPSQLIndexNamePropertyEditor); + RegisterPropertyEditor(TypeInfo(string), TPSQLStoredProc, 'StoredProcName', TPSQLStoredProcNamePropertyEditor); + RegisterPropertyEditor(TypeInfo(string), TPSQLTable, 'IndexFieldNames', TPSQLIndexFieldNamesPropertyEditor); + RegisterPropertyEditor(TypeInfo(TDataSource), TPSQLTable, 'MasterSource', TPSQLDataSourcePropertyEditor); + RegisterPropertyEditor(TypeInfo(string), TPSQLTable, 'MasterFields', TPSQLTableFieldLinkProperty); + RegisterPropertyEditor(TypeInfo(TPSQLDACAbout), nil, '', TAboutProperty); + {$IFNDEF FPC} + RegisterPropertyEditor(TypeInfo(Boolean), TBDE2PSQLDAC, 'Execute', TMigrateExecutePropertyEditor); + RegisterPropertyEditor(TypeInfo(string), TPSQLDump, 'Encoding', TPSQLDatabaseCharsetPropertyEditor); + {$ENDIF} +end; + +{$IFDEF FPC} +procedure RegisterFields(const FieldClasses: array of TPersistentClass); +begin + RegisterClasses(FieldClasses); +end; +{$ENDIF} + +procedure Register; +begin + {$IFDEF DELPHI_9} + if Assigned(SplashScreenServices) then + begin + ForceDemandLoadState(dlDisable); + SplashScreenServices.AddPluginBitmap(Format('PostgresDAC Component Suite %s', [PSQLDBTables.VERSION]), + LoadBitmap(FindResourceHInstance(HInstance), 'PSQLLOGO'), + False, + 'MIT License'); + end; + {$ENDIF} + + RegisterComponents('PostgresDAC', + [TPSQLDatabase, TPSQLTable, TPSQLQuery, TPSQLStoredProc, TPSQLUpdateSQL, TPSQLNotify, + TPSQLBatchExecute, TPSQLMacroQuery, TPSQLMonitor, TPSQLDirectQuery, + TPSQLTools, TPSQLCopy {$IFNDEF FPC},TPSQLDump, TPSQLRestore, TBDE2PSQLDAC{$ENDIF}] ); + RegisterComponentEditor(TPSQLDatabase, TPSQLDatabaseEditor); + {$IFNDEF FPC}{$IFNDEF BCB}RegisterComponentEditor(TPSQLQuery, TPSQLQueryEditor);{$ENDIF}{$ENDIF} + RegisterComponentEditor(TPSQLUpdateSQL,TPSQLUpdateSQLEditor); + RegisterComponentEditor(TPSQLStoredProc,TPSQLStoredProcEditor); + RegisterFields([TPSQLGuidField + {$IFDEF DELPHI_12} + ,TPSQLPointField, TPSQLCircleField, TPSQLBoxField, TPSQLLSegField, TPSQLRangeField + {$ENDIF DELPHI_12} + ]); + + RegisterPropertyEditors; +end; + +{ TMigrateExecutePropertyEditor } +{$IFNDEF FPC} +procedure TMigrateExecutePropertyEditor.Edit; +begin + TBDE2PSQLDAC(GetComponent(0)).Migrate; + Modified; // PaGo 23.05.2007 +end; + +function TMigrateExecutePropertyEditor.GetAttributes: TPropertyAttributes; +begin + Result := [paDialog, paReadOnly]; +end; + +function TMigrateExecutePropertyEditor.GetValue: string; +begin + Result := 'Press to Migrate...'; +end; +{$ENDIF} +{ TPSQLStoredProcNamePropertyEditor } + +function TPSQLStoredProcNamePropertyEditor.GetAttributes: TPropertyAttributes; +begin + Result := [paValueList, paSortList, paMultiSelect]; +end; + +procedure TPSQLStoredProcNamePropertyEditor.GetValueList(List: TStrings); +var + Proc: TPSQLStoredProc; +begin + Proc := GetComponent(0) as TPSQLStoredProc; + if Proc.Database = nil then raise EDatabaseError.Create('Database property is not set'); + Proc.Database.GetStoredProcNames('', List); +end; + + +procedure TPSQLStoredProcNamePropertyEditor.GetValues(Proc: TGetStrProc); +var + I : Integer; + Values : TStringList; +begin + Values := TStringList.Create; + Try + GetValueList(Values); + for I := 0 to Values.Count - 1 do Proc(Values[I]); + Finally + Values.Free; + end; +end; +{ TPSQLDatabaseCharsetPropertyEditor } + +function TPSQLDatabaseCharsetPropertyEditor.GetAttributes: TPropertyAttributes; +begin + Result := [paValueList, paSortList, paMultiSelect]; +end; + +procedure TPSQLDatabaseCharsetPropertyEditor.GetValueList(List: TStrings); +var + DB: TPSQLDatabase; + AComp: TPersistent; +begin + DB := nil; + AComp := GetComponent(0); + if AComp is TPSQLDatabase then + DB := AComp as TPSQLDatabase +{$IFNDEF FPC} + else + if (AComp is TPSQLDump) and Assigned((AComp as TPSQLDump).Database) then + DB := (AComp as TPSQLDump).Database +{$ENDIF} + else + if (AComp is TAbstractCopyObject) and Assigned((AComp as TAbstractCopyObject).Database) then + DB := (AComp as TAbstractCopyObject).Database; + if not Assigned(DB) or not DB.Connected then + List.CommaText := 'BIG5,EUC_CN,EUC_JIS_2004,EUC_JP,EUC_KR,EUC_TW,'+ + 'GB18030,GBK,ISO_8859_5,ISO_8859_6,ISO_8859_7,ISO_8859_8,JOHAB,KOI8,LATIN1,'+ + 'LATIN10,LATIN2,LATIN3,LATIN4,LATIN5,LATIN6,LATIN7,LATIN8,LATIN9,MULE_INTERNAL,'+ + 'SHIFT_JIS_2004,SJIS,SQL_ASCII,UHC,UTF8,WIN1250,WIN1251,WIN1252,WIN1253,'+ + 'WIN1254,WIN1255,WIN1256,WIN1257,WIN1258,WIN866,WIN874' + else + DB.GetCharsets(List); +end; + +procedure TPSQLDatabaseCharsetPropertyEditor.GetValues(Proc: TGetStrProc); +var + I : Integer; + Values : TStringList; +begin + Values := TStringList.Create; + Try + GetValueList(Values); + for I := 0 to Values.Count - 1 do Proc(Values[I]); + Finally + Values.Free; + end; +end; + +{ TPSQLStoredProcEditor } + +procedure TPSQLStoredProcEditor.ExecuteVerb(Index: Integer); +begin + case Index of + 0: if EditStoredProc(TPSQLStoredProc(Component)) then Designer.Modified; + end; +end; + +function TPSQLStoredProcEditor.GetVerb(Index: Integer): string; +begin + case Index of + 0: Result := 'TPSQLStoredProc Editor...'; + end; +end; + +function TPSQLStoredProcEditor.GetVerbCount: Integer; +begin + Result := 1; +end; + +{ TPSQLDataSetEditor } +{$IFNDEF FPC} +{$IFNDEF BCB} +procedure TPSQLQueryEditor.ExecuteVerb(Index: Integer); +var + SQL: string; + TableName: string; +begin + case Index of + 0: ShowFieldsEditor(Designer, TDataSet(Component), GetDSDesignerClass); + 1: + begin + FConnection := TPSQLQuery(Component).Database; + try + SQL := TPSQLQuery(Component).SQL.Text; + if SQL <> '' then + TableName := GetTableNameFromSQL(SQL); + if EditSQL(SQL, GetTables, GetFields, TableName) then + TPSQLQuery(Component).SQL.Text := SQL; + finally + FConnection := nil; + end; + end; + 2: TPSQLQuery(Component).Active := not TPSQLQuery(Component).Active; + end; + Designer.Modified; +end; + +function TPSQLQueryEditor.GetDSDesignerClass: TDSDesignerClass; +begin + Result := TPSQLDSDesigner; +end; + +procedure TPSQLQueryEditor.GetFields(const TableName: string; + List: TStrings; SystemFields: Boolean); +var S: string; +begin + List.Clear; + S := Format('SELECT quote_ident(attname) FROM pg_attribute WHERE attrelid = %s::regclass', + [QuotedStr(Tablename)]); + if not SystemFields then + S := S + ' AND attnum > 0'; + if Assigned(FConnection) and FConnection.Connected then + FConnection.SelectStrings(S, List); +end; + +procedure TPSQLQueryEditor.GetTables(List: TStrings; + SystemTables: Boolean); +begin + List.Clear; + if Assigned(FConnection) and FConnection.Connected then + FConnection.GetTableNames('', SystemTables, List); +end; + +function TPSQLQueryEditor.GetVerb(Index: Integer): string; +begin + case Index of + 0: Result := 'Fields Editor...'; + 1: Result := 'SQL Editor...'; + 2: if TPSQLQuery(Component).Active then Result := 'Close' else Result := 'Open'; + end; +end; + +function TPSQLQueryEditor.GetVerbCount: Integer; +begin + Result := 3; +end; + +{ TPSQLDSDesigner } + +procedure TPSQLDSDesigner.EndCreateFields; +var OldState: boolean; +begin + inherited; + if not (DataSet is TPSQLQuery) then Exit; + if not (dsoPopulateFieldsOrigin in TPSQLQuery(DataSet).Options) then Exit; + + OldState := TPSQLQuery(DataSet).Active; + try + TPSQLQuery(DataSet).Active := True; + TPSQLQuery(DataSet).PopulateFieldsOrigin(); + finally + TPSQLQuery(DataSet).Active := OldState; + end; +end; +{$ENDIF} +{$ENDIF} + +{ TPSQLParamOidPropertyEditor } +function TPSQLParamOidPropertyEditor.GetAttributes: TPropertyAttributes; +begin + Result := [paValueList, paMultiSelect]; +end; + +procedure TPSQLParamOidPropertyEditor.GetValueList(List: TStrings); +begin + List.Text := + '"char"=18'#13'abstime=702'#13'aclitem=1033'#13'bit varying=1562'#13'bit=1560'#13'bool=16'#13'box=603'#13'bytea=17'#13'char=1042'#13''+ + 'cid=29'#13'cidr=650'#13'circle=718'#13'date=1082'#13'float4=700'#13'float8=701'#13'inet=869'#13'int2=21'#13'int2vector=22'#13'int4=23'#13'int8=20'#13''+ + 'interval=1186'#13'line=628'#13'lseg=601'#13'macaddr=829'#13'money=790'#13'name=19'#13'numeric=1700'#13'oid=26'#13'oidvector=30'#13'path=602'#13''+ + 'point=600'#13'polygon=604'#13'refcursor=1790'#13'regclass=2205'#13'regoper=2203'#13'regoperator=2204'#13'regproc=24'#13'regprocedure=2202'#13''+ + 'regtype=2206'#13'reltime=703'#13'smgr=210'#13'text=25'#13'tid=27'#13'time with time zone=1266'#13'time=1083'#13'timestamp with time zone=1184'#13''+ + 'timestamp=1114'#13'tinterval=704'#13'tsquery=3615'#13'tsvector=3614'#13'uuid=2950'#13'varchar=1043'#13'xid=28'#13'xml=142'#13''+ + //arrays bellow + 'abstime[]=1023'#13'aclitem[]=1034'#13'bit varying[]=1563'#13'bit[]=1561'#13'bool[]=1000'#13'box[]=1020'#13'bpchar[]=1014'#13'bytea[]=1001'#13''+ + 'char[]=1002'#13'cid[]=1012'#13'cidr[]=651'#13'circle[]=719'#13'date[]=1182'#13'float4[]=1021'#13'float8[]=1022'#13'inet[]=1041'#13'int2[]=1005'#13''+ + 'int2vector[]=1006'#13'int4[]=1007'#13'int8[]=1016'#13'interval[]=1187'#13'line[]=629'#13'lseg[]=1018'#13'macaddr[]=1040'#13'money[]=791'#13''+ + 'name[]=1003'#13'numeric[]=1231'#13'oid[]=1028'#13'oidvector[]=1013'#13'path[]=1019'#13'point[]=1017'#13'polygon[]=1027'#13'refcursor[]=2201'#13''+ + 'regclass[]=2210'#13'regoper[]=2208'#13'regoperator[]=2209'#13'regproc[]=1008'#13'regprocedure[]=2207'#13'regtype[]=2211'#13'reltime[]=1024'#13''+ + 'text[]=1009'#13'tid[]=1010'#13'time with time zone[]=1270'#13'time[]=1183'#13'timestamp with time zone[]=1185'#13'timestamp[]=1115'#13''+ + 'tinterval[]=1025'#13'varchar[]=1015'#13'xid[]=1011'; +end; + +procedure TPSQLParamOidPropertyEditor.GetValues(Proc: TGetStrProc); +var + I : Integer; + Values : TStringList; +begin + Values := TStringList.Create; + Try + GetValueList(Values); + for I := 0 to Values.Count-1 do Proc(Values[I]); + Finally + Values.Free; + end; +end; + +procedure TPSQLParamOidPropertyEditor.SetValue(const Value: string); +begin + inherited SetValue(Copy(Value, Pos('=', Value) + 1, MaxInt)); +end; + +initialization + +end. diff --git a/PSQLCommon.pas b/Source/PSQLCommon.pas similarity index 96% rename from PSQLCommon.pas rename to Source/PSQLCommon.pas index c6010be..2fa7e07 100644 --- a/PSQLCommon.pas +++ b/Source/PSQLCommon.pas @@ -1,2168 +1,2168 @@ -{$I pSQLDAC.inc} -{ *************************************************************************** } -{ } -{ Kylix and Delphi Cross-Platform Visual Component Library } -{ } -{ Copyright (c) 1995, 2001 Borland Software Corporation } -{ } -{ *************************************************************************** } - - -unit PSQLCommon; - - -{SVN revision: $Id$} - -{$T-,H+,X+,R-} - -interface - -uses Windows, Classes, - {$IFDEF DELPHI_6}Variants, SqlTimSt,{$ENDIF} - {$IFDEF FPC}Variants, {$ENDIF} - DB; - -type - TCANOperator = ( - coNOTDEFINED, { } - coISBLANK, { coUnary; is operand blank. } - coNOTBLANK, { coUnary; is operand not blank. } - coEQ, { coBinary, coCompare; equal. } - coNE, { coBinary; NOT equal. } - coGT, { coBinary; greater than. } - coLT, { coBinary; less than. } - coGE, { coBinary; greater or equal. } - coLE, { coBinary; less or equal. } - coNOT, { coUnary; NOT } - coAND, { coBinary; AND } - coOR, { coBinary; OR } - coTUPLE2, { coUnary; Entire record is operand. } - coFIELD2, { coUnary; operand is field } - coCONST2, { coUnary; operand is constant } - coMINUS, { coUnary; minus. } - coADD, { coBinary; addition. } - coSUB, { coBinary; subtraction. } - coMUL, { coBinary; multiplication. } - coDIV, { coBinary; division. } - coMOD, { coBinary; modulo division. } - coREM, { coBinary; remainder of division. } - coSUM, { coBinary, accumulate sum of. } - coCOUNT, { coBinary, accumulate count of. } - coMIN, { coBinary, find minimum of. } - coMAX, { coBinary, find maximum of. } - coAVG, { coBinary, find average of. } - coCONT, { coBinary; provides a link between two } - coUDF2, { coBinary; invokes a User defined fn } - coCONTINUE2, { coUnary; Stops evaluating records } - coLIKE, { coCompare, extended binary compare } - coIN, { coBinary field in list of values } - coLIST2, { List of constant values of same type } - coUPPER, { coUnary: upper case } - coLOWER, { coUnary: lower case } - coFUNC2, { coFunc: Function } - coLISTELEM2, { coListElem: List Element } - coASSIGN { coBinary: Field assignment } - ); - - NODEClass = ( { Node Class } - nodeNULL, { Null node } - nodeUNARY, { Node is a unary } - nodeBINARY, { Node is a binary } - nodeCOMPARE, { Node is a compare } - nodeFIELD, { Node is a field } - nodeCONST, { Node is a constant } - nodeTUPLE, { Node is a record } - nodeCONTINUE, { Node is a continue node } - nodeUDF, { Node is a UDF node } - nodeLIST, { Node is a LIST node } - nodeFUNC, { Node is a Function node } - nodeLISTELEM { Node is a List Element node } - ); - -const - CANEXPRSIZE = 10; { SizeOf(CANExpr) } - CANHDRSIZE = 8; { SizeOf(CANHdr) } - CANEXPRVERSION = 2; - - -type - TExprData = array of Byte; - TFieldMap = array[TFieldType] of Byte; - - {$IFDEF FPC} - TBlobByteData = array of Byte; - {$ENDIF} - -{ TFilterExpr } - -type - - TParserOption = (poExtSyntax, poAggregate, poDefaultExpr, poUseOrigNames, - poFieldNameGiven, poFieldDepend); - TParserOptions = set of TParserOption; - - TExprNodeKind = (enField, enConst, enOperator, enFunc); - TExprScopeKind = (skField, skAgg, skConst); - - PExprNode = ^TExprNode; - TExprNode = record - FNext: PExprNode; - FKind: TExprNodeKind; - FPartial: Boolean; - FOperator: TCANOperator; - FData: Variant; - FLeft: PExprNode; - FRight: PExprNode; - FDataType: TFieldType; - FDataSize: Integer; - FArgs: TList; - FScopeKind: TExprScopeKind; - end; - - TFilterExpr = class - private - FDataSet: TDataSet; - FFieldMap: TFieldMap; - FOptions: TFilterOptions; - FParserOptions: TParserOptions; - FNodes: PExprNode; - FExprBuffer: TExprData; - FExprBufSize: Integer; - FExprNodeSize: Integer; - FExprDataSize: Integer; - FFieldName: string; - FDependentFields: TBits; - function FieldFromNode(Node: PExprNode): TField; - function GetExprData(Pos, Size: Integer): PChar; -{$IFNDEF FPC} - function PutConstBCD(const Value: Variant; Decimals: Integer): Integer; -{$ENDIF} - function PutConstBool(const Value: Variant): Integer; - function PutConstDate(const Value: Variant): Integer; - function PutConstDateTime(const Value: Variant): Integer; - function PutConstFloat(const Value: Variant): Integer; - function PutConstInt(DataType: TFieldType; const Value: Variant): Integer; - -{$IFDEF DELPHI_6} - function PutConstSQLTimeStamp(const Value: Variant): Integer; - function PutConstInt64(DataType: TFieldType; const Value: Variant): Integer; - function PutConstFMTBCD(const Value: Variant; Decimals: Integer): Integer; -{$ENDIF} - - function PutConstNode(DataType: TFieldType; Data: PAnsiChar; - Size: Integer): Integer; - function PutConstStr(const Value: string): Integer; - function PutConstTime(const Value: Variant): Integer; - function PutData(Data: PAnsiChar; Size: Integer): Integer; - function PutExprNode(Node: PExprNode; ParentOp: TCANOperator): Integer; - function PutFieldNode(Field: TField; Node: PExprNode): Integer; - function PutNode(NodeType: NodeClass; OpType: TCANOperator; - OpCount: Integer): Integer; - procedure SetNodeOp(Node, Index, Data: Integer); - function PutConstant(Node: PExprNode): Integer; - function GetFieldByName(Name: string) : TField; - public - constructor Create(DataSet: TDataSet; Options: TFilterOptions; - ParseOptions: TParserOptions; const FieldName: string; DepFields: TBits; - FieldMap: TFieldMap); - destructor Destroy; override; - function NewCompareNode(Field: TField; Operator: TCANOperator; - const Value: Variant): PExprNode; - function NewNode(Kind: TExprNodeKind; Operator: TCANOperator; - const Data: Variant; Left, Right: PExprNode): PExprNode; - function GetFilterData(Root: PExprNode): TExprData; - property DataSet: TDataSet write FDataSet; - end; - -{ TExprParser } - - TExprToken = (etEnd, etSymbol, etName, etLiteral, etLParen, etRParen, - etEQ, etNE, etGE, etLE, etGT, etLT, etADD, etSUB, etMUL, etDIV, - etComma, etLIKE, etISNULL, etISNOTNULL, etIN); - - TExprParser = class - private - FDecimalSeparator: Char; - FFilter: TFilterExpr; - FFieldMap: TFieldMap; - FText: string; - FSourcePtr: PChar; - FTokenPtr: PChar; - FTokenString: string; - FStrTrue: string; - FStrFalse: string; - FToken: TExprToken; - FPrevToken: TExprToken; - FFilterData: TExprData; - FNumericLit: Boolean; - FDataSize: Integer; - FParserOptions: TParserOptions; - FFieldName: string; - FDataSet: TDataSet; - FDependentFields: TBits; - procedure NextToken; - function NextTokenIsLParen : Boolean; - function ParseExpr: PExprNode; - function ParseExpr2: PExprNode; - function ParseExpr3: PExprNode; - function ParseExpr4: PExprNode; - function ParseExpr5: PExprNode; - function ParseExpr6: PExprNode; - function ParseExpr7: PExprNode; - function TokenName: string; - function TokenSymbolIs(const S: string): Boolean; - function TokenSymbolIsFunc(const S: string) : Boolean; - procedure GetFuncResultInfo(Node: PExprNode); - procedure TypeCheckArithOp(Node: PExprNode); - procedure GetScopeKind(Root, Left, Right : PExprNode); - public - constructor Create(DataSet: TDataSet; const Text: string; - Options: TFilterOptions; ParserOptions: TParserOptions; - const FieldName: string; DepFields: TBits; FieldMap: TFieldMap); - destructor Destroy; override; - procedure SetExprParams(const Text: string; Options: TFilterOptions; - ParserOptions: TParserOptions; const FieldName: string); - property FilterData: TExprData read FFilterData; - property DataSize: Integer read FDataSize; - end; - -{ Field Origin parser } - -type - TFieldInfo = record - DatabaseName: string; - TableName: string; - OriginalFieldName: string; - end; - -function GetFieldInfo(const Origin: string; var FieldInfo: TFieldInfo): Boolean; - -{ SQL Parser } - -type - TSQLToken = (stUnknown, stTableName, stFieldName, stAscending, stDescending, stSelect, - stFrom, stWhere, stGroupBy, stHaving, stUnion, stPlan, stOrderBy, stForUpdate, - stEnd, stPredicate, stValue, stIsNull, stIsNotNull, stLike, stAnd, stOr, - stNumber, stAllFields, stComment, stDistinct); - -const - SQLSections = [stSelect, stFrom, stWhere, stGroupBy, stHaving, stUnion, - stPlan, stOrderBy, stForUpdate]; - -function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; -function GetIndexForOrderBy(const SQL: string; DataSet: TDataSet): TIndexDef; -function GetTableNameFromSQL(const SQL: string): string; -function GetTableNameFromQuery(const SQL: string): string; -function AddParamSQLForDetail(Params: TParams; SQL: string; Native: Boolean; QuoteChar: string = ''): string; -function IsMultiTableQuery(const SQL: string): Boolean; - -resourcestring - SInvalidFieldSize = 'Invalid field size'; - SInvalidFieldKind = 'Invalid FieldKind'; - SInvalidFieldRegistration = 'Invalid field registration'; - SUnknownFieldType = 'Field ''%s'' is of an unknown type'; - SFieldNameMissing = 'Field name missing'; - SDuplicateFieldName = 'Duplicate field name ''%s'''; - SFieldNotFound = 'Field ''%s'' not found'; - SFieldAccessError = 'Cannot access field ''%s'' as type %s'; - SFieldValueError = 'Invalid value for field ''%s'''; - SFieldRangeError = '%g is not a valid value for field ''%s''. The allowed range is %g to %g'; - SBcdFieldRangeError = '%s is not a valid value for field ''%s''. The allowed range is %s to %s'; - SInvalidIntegerValue = '''%s'' is not a valid integer value for field ''%s'''; - SInvalidBoolValue = '''%s'' is not a valid boolean value for field ''%s'''; - SInvalidFloatValue = '''%s'' is not a valid floating point value for field ''%s'''; - SFieldTypeMismatch = 'Type mismatch for field ''%s'', expecting: %s actual: %s'; - SFieldSizeMismatch = 'Size mismatch for field ''%s'', expecting: %d actual: %d'; - SInvalidVarByteArray = 'Invalid variant type or size for field ''%s'''; - SFieldOutOfRange = 'Value of field ''%s'' is out of range'; -// SBCDOverflow = '(Overflow)'; - SFieldRequired = 'Field ''%s'' must have a value'; - SDataSetMissing = 'Field ''%s'' has no dataset'; - SInvalidCalcType = 'Field ''%s'' cannot be a calculated or lookup field'; - SFieldReadOnly = 'Field ''%s'' cannot be modified'; - SFieldIndexError = 'Field index out of range'; - SNoFieldIndexes = 'No index currently active'; - SNotIndexField = 'Field ''%s'' is not indexed and cannot be modified'; - SIndexFieldMissing = 'Cannot access index field ''%s'''; - SDuplicateIndexName = 'Duplicate index name ''%s'''; - SNoIndexForFields = 'No index for fields ''%s'''; - SIndexNotFound = 'Index ''%s'' not found'; - SDuplicateName = 'Duplicate name ''%s'' in %s'; - SCircularDataLink = 'Circular datalinks are not allowed'; - SLookupInfoError = 'Lookup information for field ''%s'' is incomplete'; - SNewLookupFieldCaption = 'New Lookup Field'; - SDataSourceChange = 'DataSource cannot be changed'; - SNoNestedMasterSource = 'Nested datasets cannot have a MasterSource'; - SDataSetOpen = 'Cannot perform this operation on an open dataset'; - SNotEditing = 'Dataset not in edit or insert mode'; - SDataSetClosed = 'Cannot perform this operation on a closed dataset'; - SDataSetEmpty = 'Cannot perform this operation on an empty dataset'; - SDataSetReadOnly = 'Cannot modify a read-only dataset'; - SNestedDataSetClass = 'Nested dataset must inherit from %s'; - SExprTermination = 'Filter expression incorrectly terminated'; - SExprNameError = 'Unterminated field name'; - SExprStringError = 'Unterminated string constant'; - SExprInvalidChar = 'Invalid filter expression character: ''%s'''; - SExprNoLParen = '''('' expected but %s found'; - SExprNoRParen = ''')'' expected but %s found'; - SExprNoRParenOrComma = ''')'' or '','' expected but %s found'; - SExprExpected = 'Expression expected but %s found'; - SExprBadField = 'Field ''%s'' cannot be used in a filter expression'; - SExprBadNullTest = 'NULL only allowed with ''='' and ''<>'''; - SExprRangeError = 'Constant out of range'; - SExprNotBoolean = 'Field ''%s'' is not of type Boolean'; - SExprIncorrect = 'Incorrectly formed filter expression'; - SExprNothing = 'nothing'; - SExprTypeMis = 'Type mismatch in expression'; - SExprBadScope = 'Operation cannot mix aggregate value with record-varying value'; - SExprNoArith = 'Arithmetic in filter expressions not supported'; - SExprNotAgg = 'Expression is not an aggregate expression'; - SExprBadConst = 'Constant is not correct type %s'; - SExprNoAggFilter = 'Aggregate expressions not allowed in filters'; - SExprEmptyInList = 'IN predicate list may not be empty'; - SInvalidKeywordUse = 'Invalid use of keyword'; - STextFalse = 'False'; - STextTrue = 'True'; - SParameterNotFound = 'Parameter ''%s'' not found'; - SInvalidVersion = 'Unable to load bind parameters'; - SParamTooBig = 'Parameter ''%s'', cannot save data larger than %d bytes'; - SBadFieldType = 'Field ''%s'' is of an unsupported type'; - SAggActive = 'Property may not be modified while aggregate is active'; - SProviderSQLNotSupported = 'SQL not supported: %s'; - SProviderExecuteNotSupported = 'Execute not supported: %s'; - SExprNoAggOnCalcs = 'Field ''%s'' is not the correct type of calculated field to be used in an aggregate, use an internalcalc'; - SRecordChanged = 'Record not found or changed by another user'; - SDataSetUnidirectional = 'Operation not allowed on a unidirectional dataset'; - SUnassignedVar = 'Unassigned variant value'; - SRecordNotFound = 'Record not found'; - SFileNameBlank = 'FileName property cannot be blank'; - SFieldNameTooLarge = 'Fieldname %s exceeds %d chars'; - -{ For FMTBcd } - - SBcdOverflow = 'BCD overflow'; - SInvalidBcdValue = '%s is not a valid BCD value'; - SInvalidFormatType = 'Invalid format type for BCD'; - -{ For SqlTimSt } - - SCouldNotParseTimeStamp = 'Could not parse SQL TimeStamp string'; - SInvalidSqlTimeStamp = 'Invalid SQL date/time values'; - - SDeleteRecordQuestion = 'Delete record?'; - SDeleteMultipleRecordsQuestion = 'Delete all selected records?'; - STooManyColumns = 'Grid requested to display more than 256 columns'; - - { For reconcile error } - SSkip = 'Skip'; - SAbort = 'Abort'; - SMerge = 'Merge'; - SCorrect = 'Correct'; - SCancel = 'Cancel'; - SRefresh = 'Refresh'; - SModified = 'Modified'; - SInserted = 'Inserted'; - SDeleted = 'Deleted'; - SCaption = 'Update Error - %s'; - SUnchanged = ''; - SBinary = '(Binary)'; - SAdt = '(ADT)'; - SArray = '(Array)'; - SFieldName = 'Field Name'; - SOriginal = 'Original Value'; - SConflict = 'Conflicting Value'; - SValue = ' Value'; - SNoData = ''; - SNew = 'New'; - -implementation - -uses SysUtils, - {$IFNDEF FPC} - {$IFDEF DELPHI_6}FMTBcd,{$ENDIF} - {$ENDIF} - PSQLTypes; - -{ SQL Parser } - -function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; -var - DotStart: Boolean; - - function NextTokenIs(Value: string; var Str: string): Boolean; - var - Tmp: PChar; - S: string; - begin - Tmp := p; - NextSQLToken(Tmp, S, CurSection); - Result := AnsiCompareText(Value, S) = 0; - if Result then - begin - Str := Str + ' ' + S; - p := Tmp; - end; - end; - - function GetSQLToken(var Str: string): TSQLToken; - var - l: PChar; - s: string; - begin - if Length(Str) = 0 then - Result := stEnd else - if (Str = '*') and (CurSection = stSelect) then - Result := stAllFields else - if DotStart then - Result := stFieldName else - if (AnsiCompareText('DISTINCT', Str) = 0) and (CurSection = stSelect) then - Result := stDistinct else - if (AnsiCompareText('ASC', Str) = 0) or (AnsiCompareText('ASCENDING', Str) = 0)then - Result := stAscending else - if (AnsiCompareText('DESC', Str) = 0) or (AnsiCompareText('DESCENDING', Str) = 0)then - Result := stDescending else - if AnsiCompareText('SELECT', Str) = 0 then - Result := stSelect else - if AnsiCompareText('AND', Str) = 0 then - Result := stAnd else - if AnsiCompareText('OR', Str) = 0 then - Result := stOr else - if AnsiCompareText('LIKE', Str) = 0 then - Result := stLike else - if (AnsiCompareText('IS', Str) = 0) then - begin - if NextTokenIs('NULL', Str) then - Result := stIsNull else - begin - l := p; - s := Str; - if NextTokenIs('NOT', Str) and NextTokenIs('NULL', Str) then - Result := stIsNotNull else - begin - p := l; - Str := s; - Result := stValue; - end; - end; - end else - if AnsiCompareText('FROM', Str) = 0 then - Result := stFrom else - if AnsiCompareText('WHERE', Str) = 0 then - Result := stWhere else - if (AnsiCompareText('GROUP', Str) = 0) and NextTokenIs('BY', Str) then - Result := stGroupBy else - if AnsiCompareText('HAVING', Str) = 0 then - Result := stHaving else - if AnsiCompareText('UNION', Str) = 0 then - Result := stUnion else - if AnsiCompareText('PLAN', Str) = 0 then - Result := stPlan else - if (AnsiCompareText('FOR', Str) = 0) and NextTokenIs('UPDATE', Str) then - Result := stForUpdate else - if (AnsiCompareText('ORDER', Str) = 0) and NextTokenIs('BY', Str) then - Result := stOrderBy else - if AnsiCompareText('NULL', Str) = 0 then - Result := stValue else - if CurSection = stFrom then - Result := stTableName else - Result := stFieldName; - end; - -var - TokenStart: PChar; - - procedure StartToken; - begin - if not Assigned(TokenStart) then - TokenStart := p; - end; - -var - Literal: Char; - Mark: PChar; -begin - TokenStart := nil; - DotStart := False; - while True do - begin - case p^ of - '"','''','`': - begin - StartToken; - Literal := p^; - Mark := p; - repeat Inc(p) until CharInSet(p^, [Literal,#0]); - if p^ = #0 then - begin - p := Mark; - Inc(p); - end else - begin - Inc(p); - SetString(Token, TokenStart, p - TokenStart); - Mark := PChar(Token); - Token := AnsiExtractQuotedStr(Mark, Literal); - if DotStart then - Result := stFieldName else - if p^ = '.' then - Result := stTableName else - Result := stValue; - Exit; - end; - end; - '/': - begin - StartToken; - Inc(p); - if CharInSet(p^, ['/','*']) then - begin - if p^ = '*' then - begin - repeat Inc(p) until (p = #0) or ((p^ = '*') and (p[1] = '/')); - end else - while not CharInSet(p^, [#0, #10, #13]) do Inc(p); - SetString(Token, TokenStart, p - TokenStart); - Result := stComment; - Exit; - end; - end; - ' ', #10, #13, ',', '(': - begin - if Assigned(TokenStart) then - begin - SetString(Token, TokenStart, p - TokenStart); - Result := GetSQLToken(Token); - Exit; - end else - while CharInSet(p^, [' ', #10, #13, ',', '(']) do Inc(p); - end; - '.': - begin - if Assigned(TokenStart) then - begin - SetString(Token, TokenStart, p - TokenStart); - Result := stTableName; - Exit; - end else - begin - DotStart := True; - Inc(p); - end; - end; - '=','<','>': - begin - if not Assigned(TokenStart) then - begin - TokenStart := p; - while CharInSet(p^, ['=','<','>']) do Inc(p); - SetString(Token, TokenStart, p - TokenStart); - Result := stPredicate; - Exit; - end; - Inc(p); - end; - '0'..'9': - begin - if not Assigned(TokenStart) then - begin - TokenStart := p; - while CharInSet(p^, ['0'..'9','.']) do Inc(p); - SetString(Token, TokenStart, p - TokenStart); - Result := stNumber; - Exit; - end else - Inc(p); - end; - #0: - begin - if Assigned(TokenStart) then - begin - SetString(Token, TokenStart, p - TokenStart); - Result := GetSQLToken(Token); - Exit; - end else - begin - Result := stEnd; - Token := ''; - Exit; - end; - end; - else - StartToken; - Inc(p); - end; - end; -end; - -function AddParamSQLForDetail(Params: TParams; SQL: string; Native: Boolean; QuoteChar: string = ''): string; -const - SWhere = ' where '; { do not localize } - SAnd = ' and '; { do not localize } - - function GenerateParamSQL: string; - var - I: Integer; - ParamName: string; - begin - for I := 0 to Params.Count -1 do - begin - if QuoteChar = '"' then - ParamName := '"' + StringReplace(Params[I].Name, '"', '""', [rfReplaceAll] ) + '"' - else - ParamName := QuoteChar + Params[I].Name +QuoteChar; - if I > 0 then Result := Result + SAnd; - if Native then - Result := Result + format('%s = ?', [ParamName]) - else - Result := Result + format('%s = :%s', [ParamName, ParamName]); - end; - if pos(SWhere, LowerCase(Result)) > 0 then - Result := SAnd + Result - else - Result := SWhere + Result; - end; - - function AddWhereClause: string; - var - Start: PChar; - Rest, FName: string; - SQLToken, CurSection: TSQLToken; - begin - Start := PChar(SQL); - CurSection := stUnknown; - repeat - SQLToken := NextSQLToken(Start, FName, CurSection); - until SQLToken in [stFrom, stEnd]; - if SQLToken = stFrom then - NextSQLToken(Start, FName, CurSection); - Rest := string(Start); - if Rest = '' then - Result := SQL + ' ' + GenerateParamSQL - else - Result := Copy(SQL, 1, pos(Rest, SQL)) + ' ' + GenerateParamSQL + Rest; - end; - -begin - Result := SQL; - if (Params.Count > 0) then - Result := AddWhereClause; -end; - -// SQL might be a direct tablename; -function GetTableNameFromQuery(const SQL: string): string; -begin - if pos( 'select', lowercase(SQL) ) < 1 then - Result := SQL - else - Result := GetTableNameFromSQL(SQL); -end; - -function GetTableNameFromSQL(const SQL: string): string; -var - Start: PChar; - Token: string; - SQLToken, CurSection: TSQLToken; -begin - Result := ''; - Start := PChar(SQL); - CurSection := stUnknown; - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then CurSection := SQLToken; - until SQLToken in [stEnd, stFrom]; - if SQLToken = stFrom then - begin - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then - CurSection := SQLToken else - // stValue is returned if TableNames contain quote chars. - if (SQLToken = stTableName) or (SQLToken = stValue) then - begin - Result := Token; - while (Start[0] = '.') and not (SQLToken in [stEnd]) do - begin - SQLToken := NextSqlToken(Start, Token, CurSection); - Result := Result + '.' + Token; - end; - Exit; - end; - until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]); - end; -end; - -function IsMultiTableQuery(const SQL: string): Boolean; -const - SInnerJoin = 'inner join '; { do not localize } - SOuterJoin = 'outer join '; { do not localize } -var - Start: PChar; - SResult, Token: string; - SQLToken, CurSection: TSQLToken; -begin - SResult := ''; - Start := PChar(SQL); - CurSection := stUnknown; - Result := True; - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then CurSection := SQLToken; - until SQLToken in [stEnd, stFrom]; - if SQLToken = stFrom then - begin - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then - CurSection := SQLToken else - // stValue is returned if TableNames contain quote chars. - if (SQLToken = stTableName) or (SQLToken = stValue) then - begin - SResult := Token; - while (Start[0] = '.') and not (SQLToken in [stEnd]) do - begin - SQLToken := NextSqlToken(Start, Token, CurSection); - SResult := SResult + '.' + Token; - end; - if (Start[0] = ',') or (Start[1] = ',') then - exit; - NextSqlToken(Start, Token, CurSection); - if Assigned(AnsiStrPos(Start, PChar(SInnerJoin))) or - Assigned(AnsiStrPos(Start, PChar(SOuterJoin))) then - Exit; - SQLToken := NextSqlToken(Start, Token, CurSection); - if SQLToken = stTableName then - Exit; - Result := False; - Exit; - end; - until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]); - end; -end; - -function GetIndexForOrderBy(const SQL: string; DataSet: TDataSet): TIndexDef; - - function AddField(const Fields, NewField: string): string; - begin - Result := Fields; - if Fields <> '' then - Result := Fields + ';' + NewField else - Result := NewField; - end; - -var - Start: PChar; - Token, LastField, SaveField: string; - SQLToken, CurSection: TSQLToken; - FieldIndex: Integer; -begin - Result := nil; - Start := PChar(SQL); - CurSection := stUnknown; - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then CurSection := SQLToken; - until SQLToken in [stEnd, stOrderBy]; - if SQLToken = stOrderBy then - begin - Result := TIndexDef.Create(nil); - try - LastField := ''; - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then - CurSection := SQLToken else - case SQLToken of - stTableName: ; - stFieldName: - begin - LastField := Token; - { Verify that we parsed a valid field name, not something like "UPPER(Foo)" } - if not Assigned(Dataset.FindField(LastField)) then continue; - Result.Fields := AddField(Result.Fields, LastField); - SaveField := LastField; - end; - stAscending: ; - stDescending: - Result.DescFields := AddField(Result.DescFields, SaveField); - stNumber: - begin - FieldIndex := StrToInt(Token); - if DataSet.FieldCount >= FieldIndex then - LastField := DataSet.Fields[FieldIndex - 1].FieldName else - if DataSet.FieldDefs.Count >= FieldIndex then - LastField := DataSet.FieldDefs[FieldIndex - 1].Name - else - { DB2 specific syntax "FETCH FIRST n ROWS ONLY" is blocked here, - so commenting out the following line } - //SysUtils.Abort; - continue; - Result.Fields := AddField(Result.Fields, LastField); - end; - end; - until (CurSection <> stOrderBy) or (SQLToken = stEnd); - finally - if Result.Fields = '' then - begin - Result.Free; - Result := nil; - end; - end; - end; -end; - -function GetFieldInfo(const Origin: string; var FieldInfo: TFieldInfo): Boolean; -var - Current: PChar; - Values: array[0..4] of string; - I: Integer; - - function GetPChar(const S: string): PChar; - begin - if S <> '' then Result := PChar(Pointer(S)) else Result := ''; - end; - - procedure Split(const S: string); - begin - Current := PChar(Pointer(S)); - end; - - function NextItem: string; - var - C: PChar; - I: PChar; - Terminator: Char; - Ident: array[0..1023] of Char; - begin - Result := ''; - C := Current; - I := Ident; - while CharInSet(C^, ['.',' ',#0]) do - if C^ = #0 then Exit else Inc(C); - Terminator := '.'; - if C^ = '"' then - begin - Terminator := '"'; - Inc(C); - end; - while not CharInSet(C^, [Terminator, #0]) do - begin - if CharInSet(C^, LeadBytes) then - begin - I^ := C^; - Inc(C); - Inc(I); - end - else if C^ = '\' then - begin - Inc(C); - if CharInSet(C^, LeadBytes) then - begin - I^ := C^; - Inc(C); - Inc(I); - end; - if C^ = #0 then Dec(C); - end; - I^ := C^; - Inc(C); - Inc(I); - end; - SetString(Result, Ident, I - Ident); - if (Terminator = '"') and (C^ <> #0) then Inc(C); - Current := C; - end; - - function PopValue: PChar; - begin - if I >= 0 then - begin - Result := GetPChar(Values[I]); - Dec(I); - end else Result := ''; - end; - -begin - Result := False; - if (Origin = '') then Exit; - Split(Origin); - I := -1; - repeat - Inc(I); - Values[I] := NextItem; - until (Values[I] = '') or (I = High(Values)); - if I = High(Values) then Exit; - Dec(I); - FieldInfo.OriginalFieldName := StrPas(PopValue); - FieldInfo.TableName := StrPas(PopValue); - FieldInfo.DatabaseName := StrPas(PopValue); - Result := (FieldInfo.OriginalFieldName <> '') and (FieldInfo.TableName <> ''); -end; - -const - StringFieldTypes = [ftString, ftFixedChar, ftWideString, ftGuid]; - BlobFieldTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, - ftTypedBinary, ftOraBlob, ftOraClob]; - -function IsNumeric(DataType: TFieldType): Boolean; -begin - Result := DataType in [ftSmallint, ftInteger, ftWord, ftFloat, ftCurrency, - ftBCD, ftAutoInc, ftLargeint{$IFDEF DELPHI_6}, ftFMTBcd{$ENDIF}]; -end; - -function IsTemporal(DataType: TFieldType): Boolean; -begin - Result := DataType in [ftDate, ftTime, ftDateTime{$IFDEF DELPHI_6}, ftTimeStamp{$ENDIF}]; -end; - -{ TFilterExpr } - -constructor TFilterExpr.Create(DataSet: TDataSet; Options: TFilterOptions; - ParseOptions: TParserOptions; const FieldName: string; DepFields: TBits; - FieldMap: TFieldMap); -begin - FFieldMap := FieldMap; - FDataSet := DataSet; - FOptions := Options; - FFieldName := FieldName; - FParserOptions := ParseOptions; - FDependentFields := DepFields; -end; - -destructor TFilterExpr.Destroy; -var - Node: PExprNode; -begin - SetLength(FExprBuffer, 0); - while FNodes <> nil do - begin - Node := FNodes; - FNodes := Node^.FNext; - if (Node^.FKind = enFunc) and (Node^.FArgs <> nil) then - Node^.FArgs.Free; - Dispose(Node); - end; -end; - -function TFilterExpr.FieldFromNode(Node: PExprNode): TField; -begin - Result := GetFieldByName(Node^.FData); - if not (Result.FieldKind in [fkData, fkInternalCalc]) then - DatabaseErrorFmt(SExprBadField, [Result.FieldName]); -end; - -function TFilterExpr.GetExprData(Pos, Size: Integer): PChar; -begin - SetLength(FExprBuffer, FExprBufSize + Size); - Move(FExprBuffer[Pos], FExprBuffer[Pos + Size], FExprBufSize - Pos); - Inc(FExprBufSize, Size); - Result := PChar(FExprBuffer) + Pos; -end; - -function TFilterExpr.GetFilterData(Root: PExprNode): TExprData; -begin - FExprBufSize := CANExprSize; - SetLength(FExprBuffer, FExprBufSize); - PutExprNode(Root, coNOTDEFINED); - PWord(@FExprBuffer[0])^ := CANEXPRVERSION; { iVer } - PWord(@FExprBuffer[2])^ := FExprBufSize; { iTotalSize } - PWord(@FExprBuffer[4])^ := $FFFF; { iNodes } - PWord(@FExprBuffer[6])^ := CANEXPRSIZE; { iNodeStart } - PWord(@FExprBuffer[8])^ := FExprNodeSize + CANEXPRSIZE; { iLiteralStart } - Result := FExprBuffer; -end; - -function TFilterExpr.NewCompareNode(Field: TField; Operator: TCANOperator; - const Value: Variant): PExprNode; -var - ConstExpr: PExprNode; -begin - ConstExpr := NewNode(enConst, coNOTDEFINED, Value, nil, nil); - ConstExpr^.FDataType := Field.DataType; - ConstExpr^.FDataSize := Field.Size; - Result := NewNode(enOperator, Operator, Unassigned, - NewNode(enField, coNOTDEFINED, Field.FieldName, nil, nil), ConstExpr); -end; - -function TFilterExpr.NewNode(Kind: TExprNodeKind; Operator: TCANOperator; - const Data: Variant; Left, Right: PExprNode): PExprNode; -var - Field : TField; -begin - New(Result); - with Result^ do - begin - FNext := FNodes; - FKind := Kind; - FPartial := False; - FOperator := Operator; - FData := Data; - FLeft := Left; - FRight := Right; - end; - FNodes := Result; - if Kind = enField then - begin - Field := GetFieldByName(Data); - if Field = nil then - DatabaseErrorFmt(SFieldNotFound, [Data]); - Result^.FDataType := Field.DataType; - Result^.FDataSize := Field.Size; - end; -end; - -{$IFNDEF FPC} -function TFilterExpr.PutConstBCD(const Value: Variant; - Decimals: Integer): Integer; -var - C: Currency; - BCD: TBcd; -begin - if VarType(Value) = varString then - C := StrToCurr(string(TVarData(Value).VString)) else - C := Value; - CurrToBCD(C, BCD, 32, Decimals); - Result := PutConstNode(ftBCD, @BCD, 18); -end; -{$ENDIF} - -{$IFDEF DELPHI_6} -function TFilterExpr.PutConstFMTBCD(const Value: Variant; - Decimals: Integer): Integer; -var - BCD: TBcd; -begin - - if VarType(Value) = varString then - BCD := StrToBcd(string(TVarData(Value).VString)) else - BCD := VarToBcd(Value); - Result := PutConstNode(ftBCD, @BCD, 18); -end; - -function TFilterExpr.PutConstSQLTimeStamp(const Value: Variant): Integer; -var - TimeStamp: TSQLTimeStamp; -begin - if VarType(Value) = varString then - TimeStamp := StrToSQLTimeStamp(string(TVarData(Value).VString)) else - TimeStamp := VarToSQLTimeStamp(Value); - Result := PutConstNode(ftTimeStamp, @TimeStamp, 16); -end; - -function TFilterExpr.PutConstInt64(DataType: TFieldType; - const Value: Variant): Integer; -var - IntValue: LargeInt; -begin - IntValue := Value; - Result := PutConstNode(DataType, @IntValue, SizeOf(IntValue)); -end; -{$ENDIF} - -function TFilterExpr.PutConstBool(const Value: Variant): Integer; -var - B: WordBool; -begin - B := Value; - Result := PutConstNode(ftBoolean, @B, SizeOf(WordBool)); -end; - -function TFilterExpr.PutConstDate(const Value: Variant): Integer; -var - DateTime: TDateTime; - TimeStamp: TTimeStamp; -begin - if VarType(Value) = varString then - DateTime := StrToDate(string(TVarData(Value).VString)) else - DateTime := VarToDateTime(Value); - TimeStamp := DateTimeToTimeStamp(DateTime); - Result := PutConstNode(ftDate, @TimeStamp.Date, 4); -end; - -function TFilterExpr.PutConstDateTime(const Value: Variant): Integer; -var - DateTime: TDateTime; - DateData: Double; -begin - if VarType(Value) = varString then - {$IFDEF DELPHI_7} - begin - if not TryStrToDate(string(TVarData(Value).VString), DateTime) then - DateTime := VarToDateTime(Value); - end - {$ELSE} - try - DateTime := StrToDateTime(string(TVarData(Value).VString)) - except - DateTime := VarToDateTime(Value) - end - {$ENDIF UNDER_DELPHI_6} - else - DateTime := VarToDateTime(Value); - DateData := TimeStampToMSecs(DateTimeToTimeStamp(DateTime)); - Result := PutConstNode(ftDateTime, @DateData, 8); -end; - -function TFilterExpr.PutConstFloat(const Value: Variant): Integer; -var - F: Double; -begin - if VarType(Value) = varString then - F := StrToFloat(string(TVarData(Value).VString)) else - F := Value; - Result := PutConstNode(ftFloat, @F, SizeOf(Double)); -end; - -function TFilterExpr.PutConstInt(DataType: TFieldType; - const Value: Variant): Integer; -var - I, Size: Integer; -begin - if VarType(Value) = varString then - I := StrToInt(string(TVarData(Value).VString)) else - I := Value; - Size := 2; - case DataType of - ftSmallint: - if (I < -32768) or (I > 32767) then DatabaseError(SExprRangeError); - ftWord: - if (I < 0) or (I > 65535) then DatabaseError(SExprRangeError); - else - Size := 4; - end; - Result := PutConstNode(DataType, @I, Size); -end; - -function TFilterExpr.PutConstNode(DataType: TFieldType; Data: PAnsiChar; - Size: Integer): Integer; -begin - Result := PutNode(nodeCONST, coCONST2, 3); - SetNodeOp(Result, 0, FFieldMap[DataType]); - SetNodeOp(Result, 1, Size); - SetNodeOp(Result, 2, PutData(Data, Size)); -end; - -function TFilterExpr.PutConstStr(const Value: string): Integer; -var - Str: string; - Buffer: array[0..255] of AnsiChar; -begin - if Length(Value) >= SizeOf(Buffer) then - Str := Copy(Value, 1, SizeOf(Buffer) - 1) else - Str := Value; - FDataSet.Translate(PAnsiChar(AnsiString(Str)), Buffer, True); - Result := PutConstNode(ftString, Buffer, Length(Str) + 1); -end; - -function TFilterExpr.PutConstTime(const Value: Variant): Integer; -var - DateTime: TDateTime; - TimeStamp: TTimeStamp; -begin - if VarType(Value) = varString then - DateTime := StrToTime(string(TVarData(Value).VString)) else - DateTime := VarToDateTime(Value); - TimeStamp := DateTimeToTimeStamp(DateTime); - Result := PutConstNode(ftTime, @TimeStamp.Time, 4); -end; - -function TFilterExpr.PutData(Data: PAnsiChar; Size: Integer): Integer; -begin - Move(Data^, GetExprData(FExprBufSize, Size)^, Size); - Result := FExprDataSize; - Inc(FExprDataSize, Size); -end; - -function TFilterExpr.PutConstant(Node: PExprNode): Integer; -begin - Result := 0; - case Node^.FDataType of - ftSmallInt, ftInteger, ftWord, ftAutoInc: - Result := PutConstInt(Node^.FDataType, Node^.FData); - ftFloat, ftCurrency: - Result := PutConstFloat(Node^.FData); - ftString, ftWideString, ftFixedChar, ftGuid: - Result := PutConstStr(Node^.FData); - ftDate: - Result := PutConstDate(Node^.FData); - ftTime: - Result := PutConstTime(Node^.FData); - ftDateTime: - Result := PutConstDateTime(Node^.FData); - ftBoolean: - Result := PutConstBool(Node^.FData); -{$IFDEF DELPHI_6} - ftTimeStamp: - Result := PutConstSQLTimeStamp(Node^.FData); - ftFMTBcd: - Result := PutConstFMTBCD(Node^.FData, Node^.FDataSize); - ftLargeint: - Result := PutConstInt64(Node^.FDataType, Node^.FData); -{$ENDIF} -{$IFNDEF FPC} - ftBCD: - Result := PutConstBCD(Node^.FData, Node^.FDataSize); -{$ENDIF} - else - DatabaseErrorFmt(SExprBadConst, [Node^.FData]); - end; -end; - - -function TFilterExpr.PutExprNode(Node: PExprNode; ParentOp: TCANOperator): Integer; -const - ReverseOperator: array[coEQ..coLE] of TCANOperator = (coEQ, coNE, coLT, - coGT, coLE, coGE); - BoolFalse: WordBool = False; -var - Field: TField; - Left, Right, Temp : PExprNode; - LeftPos, RightPos, ListElem, PrevListElem, I: Integer; - Operator: TCANOperator; - CaseInsensitive, PartialLength, L: Integer; - S: string; -begin - Result := 0; - case Node^.FKind of - enField: - begin - Field := FieldFromNode(Node); - if (ParentOp in [coOR, coNOT, coAND, coNOTDEFINED]) and - (Field.DataType = ftBoolean) then - begin - Result := PutNode(nodeBINARY, coNE, 2); - SetNodeOp(Result, 0, PutFieldNode(Field, Node)); - SetNodeOp(Result, 1, PutConstNode(ftBoolean, @BoolFalse, SizeOf(WordBool))); - end - else - Result := PutFieldNode(Field, Node); - end; - enConst: - Result := PutConstant(Node); - enOperator: - case Node^.FOperator of - coIN: - begin - Result := PutNode(nodeBINARY, coIN, 2); - SetNodeOp(Result, 0, PutExprNode(Node^.FLeft,Node^.FOperator)); - ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); - SetNodeOp(Result, 1, ListElem); - PrevListElem := ListElem; - for I := 0 to Node^.FArgs.Count - 1 do - begin - LeftPos := PutExprNode(Node^.FArgs.Items[I],Node^.FOperator); - if I = 0 then - begin - SetNodeOp(PrevListElem, 0, LeftPos); - SetNodeOp(PrevListElem, 1, 0); - end - else - begin - ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); - SetNodeOp(ListElem, 0, LeftPos); - SetNodeOp(ListElem, 1, 0); - SetNodeOp(PrevListElem, 1, ListElem); - PrevListElem := ListElem; - end; - end; - end; - coNOT, - coISBLANK, - coNOTBLANK: - begin - Result := PutNode(nodeUNARY, Node^.FOperator, 1); - SetNodeOp(Result, 0, PutExprNode(Node^.FLeft,Node^.FOperator)); - end; - coEQ..coLE, - coAND,coOR, - coADD..coDIV, - coLIKE, - coASSIGN: - begin - Operator := Node^.FOperator; - Left := Node^.FLeft; - Right := Node^.FRight; - if (Operator in [coEQ..coLE]) and (Right^.FKind = enField) and - (Left^.FKind <> enField) then - begin - Temp := Left; - Left := Right; - Right := Temp; - Operator := ReverseOperator[Operator]; - end; - - Result := 0; - if (Left^.FKind = enField) and (Right^.FKind = enConst) - and ((Node^.FOperator = coEQ) or (Node^.FOperator = coNE) - or (Node^.FOperator = coLIKE)) then - begin - if VarIsNull(Right^.FData) then - begin - case Node^.FOperator of - coEQ: Operator := coISBLANK; - coNE: Operator := coNOTBLANK; - else - DatabaseError(SExprBadNullTest); - end; - Result := PutNode(nodeUNARY, Operator, 1); - SetNodeOp(Result, 0, PutExprNode(Left,Node^.FOperator)); - end - else if (Right^.FDataType in StringFieldTypes) then - begin - S := Right^.FData; - L := Length(S); - if L <> 0 then - begin - CaseInsensitive := 0; - PartialLength := 0; - if foCaseInsensitive in FOptions then CaseInsensitive := 1; - if Node^.FPartial then PartialLength := L else - if not (foNoPartialCompare in FOptions) and (L > 1) and - (S[L] = '*') then - begin - Delete(S, L, 1); - PartialLength := L - 1; - end; - if (CaseInsensitive <> 0) or (PartialLength <> 0) then - begin - Result := PutNode(nodeCOMPARE, Operator, 4); - SetNodeOp(Result, 0, CaseInsensitive); - SetNodeOp(Result, 1, PartialLength); - SetNodeOp(Result, 2, PutExprNode(Left,Node^.FOperator)); - SetNodeOp(Result, 3, PutConstStr(S)); - end; - end; - end; - end; - - if Result = 0 then - begin - if (Operator = coISBLANK) or (Operator = coNOTBLANK) then - begin - Result := PutNode(nodeUNARY, Operator, 1); - LeftPos := PutExprNode(Left,Node^.FOperator); - SetNodeOp(Result, 0, LeftPos); - end else - begin - Result := PutNode(nodeBINARY, Operator, 2); - LeftPos := PutExprNode(Left,Node^.FOperator); - RightPos := PutExprNode(Right,Node^.FOperator); - SetNodeOp(Result, 0, LeftPos); - SetNodeOp(Result, 1, RightPos); - end; - end; - end; - end; - enFunc: - begin - Result := PutNode(nodeFUNC, coFUNC2, 2); - SetNodeOp(Result, 0, PutData(PAnsiChar(ansistring(Node^.FData)), - Length(string(Node^.FData)) + 1)); - if Node^.FArgs <> nil then - begin - ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); - SetNodeOp(Result, 1, ListElem); - PrevListElem := ListElem; - for I := 0 to Node^.FArgs.Count - 1 do - begin - LeftPos := PutExprNode(Node^.FArgs.Items[I],Node^.FOperator); - if I = 0 then - begin - SetNodeOp(PrevListElem, 0, LeftPos); - SetNodeOp(PrevListElem, 1, 0); - end - else - begin - ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); - SetNodeOp(ListElem, 0, LeftPos); - SetNodeOp(ListElem, 1, 0); - SetNodeOp(PrevListElem, 1, ListElem); - PrevListElem := ListElem; - end; - end; - end else - SetNodeOp(Result, 1, 0); - end; - end; -end; - - -function TFilterExpr.PutFieldNode(Field: TField; Node: PExprNode): Integer; -var - Buffer: array[0..255] of ansiChar; -begin - if poFieldNameGiven in FParserOptions then - FDataSet.Translate(PAnsiChar(Ansistring(Field.FieldName)), Buffer, True) - else - FDataSet.Translate(PAnsiChar(Ansistring(Node^.FData)), Buffer, True); - Result := PutNode(nodeFIELD, coFIELD2, 2); - SetNodeOp(Result, 0, Field.FieldNo); - SetNodeOp(Result, 1, PutData(Buffer, StrLen(Buffer) + 1)); -end; - -function TFilterExpr.PutNode(NodeType: NodeClass; OpType: TCANOperator; - OpCount: Integer): Integer; -var - Size: Integer; - Data: PChar; -begin - Size := CANHDRSIZE + OpCount * SizeOf(Word); - Data := GetExprData(CANEXPRSIZE + FExprNodeSize, Size); - PInteger(@Data[0])^ := Integer(NodeType); { CANHdr.nodeClass } - PInteger(@Data[4])^ := Integer(OpType); { CANHdr.coOp } - Result := FExprNodeSize; - Inc(FExprNodeSize, Size); -end; - -procedure TFilterExpr.SetNodeOp(Node, Index, Data: Integer); -begin - PWordArray(PChar(FExprBuffer) + (CANEXPRSIZE + Node + - CANHDRSIZE))^[Index] := Data; -end; - -function TFilterExpr.GetFieldByName(Name: string) : TField; -var - I: Integer; - F: TField; - FieldInfo: TFieldInfo; -begin - Result := nil; - if poFieldNameGiven in FParserOptions then - Result := FDataSet.FieldByName(FFieldName) - else if poUseOrigNames in FParserOptions then - begin - for I := 0 to FDataset.FieldCount - 1 do - begin - F := FDataSet.Fields[I]; - if GetFieldInfo(F.Origin, FieldInfo) and - (AnsiCompareStr(Name, FieldInfo.OriginalFieldName) = 0) then - begin - Result := F; - Exit; - end; - end; - end; - if Result = nil then - Result := FDataSet.FieldByName(Name); - if (Result <> nil) and (Result.FieldKind = fkCalculated) and (poAggregate in FParserOptions) then - DatabaseErrorFmt(SExprNoAggOnCalcs, [Result.FieldName]); - if (poFieldDepend in FParserOptions) and (Result <> nil) and - (FDependentFields <> nil) then - FDependentFields[Result.FieldNo-1] := True; -end; - -constructor TExprParser.Create(DataSet: TDataSet; const Text: string; - Options: TFilterOptions; ParserOptions: TParserOptions; const FieldName: string; - DepFields: TBits; FieldMap: TFieldMap); -begin - FDecimalSeparator := PSQLTypes.PSQL_FS.DecimalSeparator; - FFieldMap := FieldMap; - FStrTrue := STextTrue; - FStrFalse := STextFalse; - FDataSet := DataSet; - FDependentFields := DepFields; - FFilter := TFilterExpr.Create(DataSet, Options, ParserOptions, FieldName, - DepFields, FieldMap); - if Text <> '' then - SetExprParams(Text, Options, ParserOptions, FieldName); -end; - -destructor TExprParser.Destroy; -begin - FFilter.Free; -end; - -procedure TExprParser.SetExprParams(const Text: string; Options: TFilterOptions; - ParserOptions: TParserOptions; const FieldName: string); -var - Root, DefField: PExprNode; -begin - FParserOptions := ParserOptions; - if FFilter <> nil then - FFilter.Free; - FFilter := TFilterExpr.Create(FDataSet, Options, ParserOptions, FieldName, - FDependentFields, FFieldMap); - FText := Text; - FSourcePtr := PChar(Text); - FFieldName := FieldName; - NextToken; - Root := ParseExpr; - if FToken <> etEnd then DatabaseError(SExprTermination); - if (poAggregate in FParserOptions) and (Root^.FScopeKind <> skAgg) then - DatabaseError(SExprNotAgg); - if (not (poAggregate in FParserOptions)) and (Root^.FScopeKind = skAgg) then - DatabaseError(SExprNoAggFilter); - if poDefaultExpr in ParserOptions then - begin - DefField := FFilter.NewNode(enField, coNOTDEFINED, FFieldName, nil, nil); - if (IsTemporal(DefField^.FDataType) and (Root^.FDataType in StringFieldTypes)) or - ((DefField^.FDataType = ftBoolean ) and (Root^.FDataType in StringFieldTypes)) then - Root^.FDataType := DefField^.FDataType; - - if not ((IsTemporal(DefField^.FDataType) and IsTemporal(Root^.FDataType)) - or (IsNumeric(DefField^.FDataType) and IsNumeric(Root^.FDataType)) - or ((DefField^.FDataType in StringFieldTypes) and (Root^.FDataType in StringFieldTypes)) - or ((DefField^.FDataType = ftBoolean) and (Root^.FDataType = ftBoolean))) then - DatabaseError(SExprTypeMis); - Root := FFilter.NewNode(enOperator, coASSIGN, Unassigned, Root, DefField); - end; - - if not (poAggregate in FParserOptions) and not(poDefaultExpr in ParserOptions) - and (Root^.FDataType <> ftBoolean ) then - DatabaseError(SExprIncorrect); - - FFilterData := FFilter.GetFilterData(Root); - FDataSize := FFilter.FExprBufSize; -end; - -function TExprParser.NextTokenIsLParen : Boolean; -var - P : PChar; -begin - P := FSourcePtr; - while (P^ <> #0) and (P^ <= ' ') do Inc(P); - Result := P^ = '('; -end; - -function EndOfLiteral(var P : PChar): Boolean; -var - FName: String; - PTemp: PChar; -begin - Inc(P); - Result := P^ <> ''''; - if Result then - begin // now, look for 'John's Horse' - if AnsiStrScan(P, '''') <> Nil then // found another ' - begin - PTemp := P; // don't advance P - while CharInSet(PTemp[0], [ ' ', ')' ]) do Inc(PTemp); - if NextSQLToken(PTemp, FName, stValue) in [stFieldName, stUnknown] then - begin // 'John's Horse' case: not really end of literal - Result := False; - Dec(P); - end; - end; - end; -end; - -procedure TExprParser.NextToken; -type - ASet = Set of AnsiChar; -var - P, TokenStart: PChar; - L: Integer; - StrBuf: array[0..255] of Char; - - function IsKatakana(const Chr: Byte): Boolean; - begin - Result := (SysLocale.PriLangID = LANG_JAPANESE) and (Chr in [$A1..$DF]); - end; - - procedure Skip(TheSet: ASet); - begin - while TRUE do - begin - if CharInSet(P^, LeadBytes) then - Inc(P, 2) - else if CharInSet(P^, TheSet) or IsKatakana(Byte(P^)) then - Inc(P) - else - Exit; - end; - end; - -begin - FPrevToken := FToken; - FTokenString := ''; - P := FSourcePtr; - while (P^ <> #0) and (P^ <= ' ') do Inc(P); - if (P^ <> #0) and (P^ = '/') and (P[1] <> #0) and (P[1] = '*')then - begin - P := P + 2; - while (P^ <> #0) and (P^ <> '*') do Inc(P); - if (P^ = '*') and (P[1] <> #0) and (P[1] = '/') then - P := P + 2 - else - DatabaseErrorFmt(SExprInvalidChar, [P^]); - end; - while (P^ <> #0) and (P^ <= ' ') do Inc(P); - FTokenPtr := P; - case P^ of - 'A'..'Z', 'a'..'z', '_', #$81..#$fe: - begin - TokenStart := P; - if not SysLocale.FarEast then - begin - Inc(P); - while CharInSet(P^, ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '[', ']']) do Inc(P); - end - else - Skip(['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '[', ']']); - SetString(FTokenString, TokenStart, P - TokenStart); - FToken := etSymbol; - if CompareText(FTokenString, 'LIKE') = 0 then { do not localize } - FToken := etLIKE - else if CompareText(FTokenString, 'IN') = 0 then { do not localize } - FToken := etIN - else if CompareText(FTokenString, 'IS') = 0 then { do not localize } - begin - while (P^ <> #0) and (P^ <= ' ') do Inc(P); - TokenStart := P; - Skip(['A'..'Z', 'a'..'z']); - SetString(FTokenString, TokenStart, P - TokenStart); - if CompareText(FTokenString, 'NOT')= 0 then { do not localize } - begin - while (P^ <> #0) and (P^ <= ' ') do Inc(P); - TokenStart := P; - Skip(['A'..'Z', 'a'..'z']); - SetString(FTokenString, TokenStart, P - TokenStart); - if CompareText(FTokenString, 'NULL') = 0 then - FToken := etISNOTNULL - else - DatabaseError(SInvalidKeywordUse); - end - else if CompareText (FTokenString, 'NULL') = 0 then { do not localize } - begin - FToken := etISNULL; - end - else - DatabaseError(SInvalidKeywordUse); - end; - end; - '[': - begin - Inc(P); - TokenStart := P; - P := AnsiStrScan(P, ']'); - if P = nil then DatabaseError(SExprNameError); - SetString(FTokenString, TokenStart, P - TokenStart); - FToken := etName; - Inc(P); - end; - '''': - begin - Inc(P); - L := 0; - while True do - begin - if P^ = #0 then DatabaseError(SExprStringError); - if P^ = '''' then - if EndOfLiteral(P) then - Break; - if L < SizeOf(StrBuf) then - begin - StrBuf[L] := P^; - Inc(L); - end; - Inc(P); - end; - SetString(FTokenString, StrBuf, L); - FToken := etLiteral; - FNumericLit := False; - end; - '-', '0'..'9': - begin - if (FPrevToken <> etLiteral) and (FPrevToken <> etName) and - (FPrevToken <> etSymbol)and (FPrevToken <> etRParen) then - begin - TokenStart := P; - Inc(P); - while CharInSet(P^, ['0'..'9', FDecimalSeparator, 'e', 'E', '+', '-']) do - Inc(P); - if ((P-1)^ = ',') and (FDecimalSeparator = ',') and (P^ = ' ') then - Dec(P); - SetString(FTokenString, TokenStart, P - TokenStart); - FToken := etLiteral; - FNumericLit := True; - end - else - begin - FToken := etSUB; - Inc(P); - end; - end; - '(': - begin - Inc(P); - FToken := etLParen; - end; - ')': - begin - Inc(P); - FToken := etRParen; - end; - '<': - begin - Inc(P); - case P^ of - '=': - begin - Inc(P); - FToken := etLE; - end; - '>': - begin - Inc(P); - FToken := etNE; - end; - else - FToken := etLT; - end; - end; - '=': - begin - Inc(P); - FToken := etEQ; - end; - '>': - begin - Inc(P); - if P^ = '=' then - begin - Inc(P); - FToken := etGE; - end else - FToken := etGT; - end; - '+': - begin - Inc(P); - FToken := etADD; - end; - '*': - begin - Inc(P); - FToken := etMUL; - end; - '/': - begin - Inc(P); - FToken := etDIV; - end; - ',': - begin - Inc(P); - FToken := etComma; - end; - #0: - FToken := etEnd; - else - DatabaseErrorFmt(SExprInvalidChar, [P^]); - end; - FSourcePtr := P; -end; - -function TExprParser.ParseExpr: PExprNode; -begin - Result := ParseExpr2; - while TokenSymbolIs('OR') do - begin - NextToken; - Result := FFilter.NewNode(enOperator, coOR, Unassigned, - Result, ParseExpr2); - GetScopeKind(Result, Result^.FLeft, Result^.FRight); - Result^.FDataType := ftBoolean; - end; -end; - -function TExprParser.ParseExpr2: PExprNode; -begin - Result := ParseExpr3; - while TokenSymbolIs('AND') do - begin - NextToken; - Result := FFilter.NewNode(enOperator, coAND, Unassigned, - Result, ParseExpr3); - GetScopeKind(Result, Result^.FLeft, Result^.FRight); - Result^.FDataType := ftBoolean; - end; -end; - -function TExprParser.ParseExpr3: PExprNode; -begin - if TokenSymbolIs('NOT') then - begin - NextToken; - Result := FFilter.NewNode(enOperator, coNOT, Unassigned, - ParseExpr4, nil); - Result^.FDataType := ftBoolean; - end else - Result := ParseExpr4; - GetScopeKind(Result, Result^.FLeft, Result^.FRight); -end; - - -function TExprParser.ParseExpr4: PExprNode; -const - Operators: array[etEQ..etLT] of TCANOperator = ( - coEQ, coNE, coGE, coLE, coGT, coLT); -var - Operator: TCANOperator; - Left, Right: PExprNode; -begin - Result := ParseExpr5; - if (FToken in [etEQ..etLT]) or (FToken = etLIKE) - or (FToken = etISNULL) or (FToken = etISNOTNULL) - or (FToken = etIN) then - begin - case FToken of - etEQ..etLT: - Operator := Operators[FToken]; - etLIKE: - Operator := coLIKE; - etISNULL: - Operator := coISBLANK; - etISNOTNULL: - Operator := coNOTBLANK; - etIN: - Operator := coIN; - else - Operator := coNOTDEFINED; - end; - NextToken; - Left := Result; - if Operator = coIN then - begin - if FToken <> etLParen then - DatabaseErrorFmt(SExprNoLParen, [TokenName]); - NextToken; - Result := FFilter.NewNode(enOperator, coIN, Unassigned, - Left, nil); - Result.FDataType := ftBoolean; - if FToken <> etRParen then - begin - Result.FArgs := TList.Create; - repeat - Right := ParseExpr; - if IsTemporal(Left.FDataType) then - Right.FDataType := Left.FDataType; - Result.FArgs.Add(Right); - if (FToken <> etComma) and (FToken <> etRParen) then - DatabaseErrorFmt(SExprNoRParenOrComma, [TokenName]); - if FToken = etComma then NextToken; - until (FToken = etRParen) or (FToken = etEnd); - if FToken <> etRParen then - DatabaseErrorFmt(SExprNoRParen, [TokenName]); - NextToken; - end else - DatabaseError(SExprEmptyInList); - end else - begin - if (Operator <> coISBLANK) and (Operator <> coNOTBLANK) then - Right := ParseExpr5 - else - Right := nil; - Result := FFilter.NewNode(enOperator, Operator, Unassigned, - Left, Right); - if Right <> nil then - begin - if (Left^.FKind = enField) and (Right^.FKind = enConst) then - begin - Right^.FDataType := Left^.FDataType; - Right^.FDataSize := Left^.FDataSize; - end - else if (Right^.FKind = enField) and (Left^.FKind = enConst) then - begin - Left^.FDataType := Right^.FDataType; - Left^.FDataSize := Right^.FDataSize; - end; - end; - if (Left^.FDataType in BlobFieldTypes) and (Operator = coLIKE) then - begin - if Right^.FKind = enConst then Right^.FDataType := ftString; - end - else if (Operator <> coISBLANK) and (Operator <> coNOTBLANK) - and ((Left^.FDataType in (BlobFieldTypes + [ftBytes])) or - ((Right <> nil) and (Right^.FDataType in (BlobFieldTypes + [ftBytes])))) then - DatabaseError(SExprTypeMis); - Result.FDataType := ftBoolean; - if Right <> nil then - begin - if IsTemporal(Left.FDataType) and (Right.FDataType in StringFieldTypes) then - Right.FDataType := Left.FDataType - else if IsTemporal(Right.FDataType) and (Left.FDataType in StringFieldTypes) then - Left.FDataType := Right.FDataType; - end; - GetScopeKind(Result, Left, Right); - end; - end; -end; - -function TExprParser.ParseExpr5: PExprNode; -const - Operators: array[etADD..etDIV] of TCANOperator = ( - coADD, coSUB, coMUL, coDIV); -var - Operator: TCANOperator; - Left, Right: PExprNode; -begin - Result := ParseExpr6; - while FToken in [etADD, etSUB] do - begin - if not (poExtSyntax in FParserOptions) then - DatabaseError(SExprNoArith); - Operator := Operators[FToken]; - Left := Result; - NextToken; - Right := ParseExpr6; - Result := FFilter.NewNode(enOperator, Operator, Unassigned, Left, Right); - TypeCheckArithOp(Result); - GetScopeKind(Result, Left, Right); - end; -end; - -function TExprParser.ParseExpr6: PExprNode; -const - Operators: array[etADD..etDIV] of TCANOperator = ( - coADD, coSUB, coMUL, coDIV); -var - Operator: TCANOperator; - Left, Right: PExprNode; -begin - Result := ParseExpr7; - while FToken in [etMUL, etDIV] do - begin - if not (poExtSyntax in FParserOptions) then - DatabaseError(SExprNoArith); - Operator := Operators[FToken]; - Left := Result; - NextToken; - Right := ParseExpr7; - Result := FFilter.NewNode(enOperator, Operator, Unassigned, Left, Right); - TypeCheckArithOp(Result); - GetScopeKind(Result, Left, Right); - end; -end; - - -function TExprParser.ParseExpr7: PExprNode; -var - FuncName: string; -begin - case FToken of - etSymbol: - if (poExtSyntax in FParserOptions) - and NextTokenIsLParen and TokenSymbolIsFunc(FTokenString) then - begin - Funcname := FTokenString; - NextToken; - if FToken <> etLParen then - DatabaseErrorFmt(SExprNoLParen, [TokenName]); - NextToken; - if (CompareText(FuncName,'count') = 0) and (FToken = etMUL) then - begin - FuncName := 'COUNT(*)'; - NextToken; - end; - Result := FFilter.NewNode(enFunc, coNOTDEFINED, FuncName, - nil, nil); - if FToken <> etRParen then - begin - Result.FArgs := TList.Create; - repeat - Result.FArgs.Add(ParseExpr); - if (FToken <> etComma) and (FToken <> etRParen) then - DatabaseErrorFmt(SExprNoRParenOrComma, [TokenName]); - if FToken = etComma then NextToken; - until (FToken = etRParen) or (FToken = etEnd); - end else - Result.FArgs := nil; - - GetFuncResultInfo(Result); - end - else if TokenSymbolIs('NULL') then - begin - Result := FFilter.NewNode(enConst, coNOTDEFINED, {$IFDEF DELPHI_6}Variants.{$ENDIF}Null, nil, nil); - Result.FScopeKind := skConst; - end - else if TokenSymbolIs(FStrTrue) then - begin - Result := FFilter.NewNode(enConst, coNOTDEFINED, 1, nil, nil); - Result.FScopeKind := skConst; - end - else if TokenSymbolIs(FStrFalse) then - begin - Result := FFilter.NewNode(enConst, coNOTDEFINED, 0, nil, nil); - Result.FScopeKind := skConst; - end - else - begin - Result := FFilter.NewNode(enField, coNOTDEFINED, FTokenString, nil, nil); - Result.FScopeKind := skField; - end; - etName: - begin - Result := FFilter.NewNode(enField, coNOTDEFINED, FTokenString, nil, nil); - Result.FScopeKind := skField; - end; - etLiteral: - begin - Result := FFilter.NewNode(enConst, coNOTDEFINED, FTokenString, nil, nil); - if FNumericLit then Result^.FDataType := ftFloat else - Result^.FDataType := ftString; - Result.FScopeKind := skConst; - end; - etLParen: - begin - NextToken; - Result := ParseExpr; - if FToken <> etRParen then DatabaseErrorFmt(SExprNoRParen, [TokenName]); - end; - else - DatabaseErrorFmt(SExprExpected, [TokenName]); - Result := nil; - end; - NextToken; -end; - -procedure TExprParser.GetScopeKind(Root, Left, Right : PExprNode); -begin - if (Left = nil) and (Right = nil) then Exit; - if Right = nil then - begin - Root.FScopeKind := Left.FScopeKind; - Exit; - end; - if ((Left^.FScopeKind = skField) and (Right^.FScopeKind = skAgg)) - or ((Left^.FScopeKind = skAgg) and (Right^.FScopeKind = skField)) then - DatabaseError(SExprBadScope); - if (Left^.FScopeKind = skConst) and (Right^.FScopeKind = skConst) then - Root^.FScopeKind := skConst - else if (Left^.FScopeKind = skAgg) or (Right^.FScopeKind = skAgg) then - Root^.FScopeKind := skAgg - else if (Left^.FScopeKind = skField) or (Right^.FScopeKind = skField) then - Root^.FScopeKind := skField; -end; - -procedure TExprParser.GetFuncResultInfo(Node : PExprNode); -begin - Node^.FDataType := ftString; - if (CompareText(Node^.FData, 'COUNT(*)') <> 0 ) - and (CompareText(Node^.FData,'GETDATE') <> 0 ) - and ( (Node^.FArgs = nil ) or ( Node^.FArgs.Count = 0) ) then - DatabaseError(SExprTypeMis); - - if (Node^.FArgs <> nil) and (Node^.FArgs.Count > 0) then - Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; - if (CompareText(Node^.FData , 'SUM') = 0) or - (CompareText(Node^.FData , 'AVG') = 0) then - begin - Node^.FDataType := ftFloat; - Node^.FScopeKind := skAgg; - end - else if (CompareText(Node^.FData , 'MIN') = 0) or - (CompareText(Node^.FData , 'MAX') = 0) then - begin - Node^.FDataType := PExprNode(Node^.FArgs.Items[0])^.FDataType; - Node^.FScopeKind := skAgg; - end - else if (CompareText(Node^.FData , 'COUNT') = 0) or - (CompareText(Node^.FData , 'COUNT(*)') = 0) then - begin - Node^.FDataType := ftInteger; - Node^.FScopeKind := skAgg; - end - else if (CompareText(Node^.FData , 'YEAR') = 0) or - (CompareText(Node^.FData , 'MONTH') = 0) or - (CompareText(Node^.FData , 'DAY') = 0) or - (CompareText(Node^.FData , 'HOUR') = 0) or - (CompareText(Node^.FData , 'MINUTE') = 0) or - (CompareText(Node^.FData , 'SECOND') = 0 ) then - begin - Node^.FDataType := ftInteger; - Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; - end - else if CompareText(Node^.FData , 'GETDATE') = 0 then - begin - Node^.FDataType := ftDateTime; - Node^.FScopeKind := skConst; - end - else if CompareText(Node^.FData , 'DATE') = 0 then - begin - Node^.FDataType := ftDate; - Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; - end - else if CompareText(Node^.FData , 'TIME') = 0 then - begin - Node^.FDataType := ftTime; - Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; - end; -end; - -function TExprParser.TokenName: string; -begin - if FSourcePtr = FTokenPtr then Result := SExprNothing else - begin - SetString(Result, FTokenPtr, FSourcePtr - FTokenPtr); - Result := '''' + Result + ''''; - end; -end; - -function TExprParser.TokenSymbolIs(const S: string): Boolean; -begin - Result := (FToken = etSymbol) and (CompareText(FTokenString, S) = 0); -end; - - -function TExprParser.TokenSymbolIsFunc(const S: string) : Boolean; -begin - Result := (CompareText(S, 'UPPER') = 0) or - (CompareText(S, 'LOWER') = 0) or - (CompareText(S, 'SUBSTRING') = 0) or - (CompareText(S, 'TRIM') = 0) or - (CompareText(S, 'TRIMLEFT') = 0) or - (CompareText(S, 'TRIMRIGHT') = 0) or - (CompareText(S, 'YEAR') = 0) or - (CompareText(S, 'MONTH') = 0) or - (CompareText(S, 'DAY') = 0) or - (CompareText(S, 'HOUR') = 0) or - (CompareText(S, 'MINUTE') = 0) or - (CompareText(S, 'SECOND') = 0) or - (CompareText(S, 'GETDATE') = 0) or - (CompareText(S, 'DATE') = 0) or - (CompareText(S, 'TIME') = 0) or - (CompareText(S, 'SUM') = 0) or - (CompareText(S, 'MIN') = 0) or - (CompareText(S, 'MAX') = 0) or - (CompareText(S, 'AVG') = 0) or - (CompareText(S, 'COUNT') = 0); - -end; - -procedure TExprParser.TypeCheckArithOp(Node: PExprNode); -begin - with Node^ do - begin - if IsNumeric(FLeft.FDataType) and IsNumeric(FRight.FDataType) then - FDataType := ftFloat - else if (FLeft.FDataType in StringFieldTypes) and - (FRight.FDataType in StringFieldTypes) and (FOperator = coADD) then - FDataType := ftString - else if IsTemporal(FLeft.FDataType) and IsNumeric(FRight.FDataType) and - (FOperator = coADD) then - FDataType := ftDateTime - else if IsTemporal(FLeft.FDataType) and IsNumeric(FRight.FDataType) and - (FOperator = coSUB) then - FDataType := FLeft.FDataType - else if IsTemporal(FLeft.FDataType) and IsTemporal(FRight.FDataType) and - (FOperator = coSUB) then - FDataType := ftFloat - else if (FLeft.FDataType in StringFieldTypes) and IsTemporal(FRight.FDataType) and - (FOperator = coSUB) then - begin - FLeft.FDataType := FRight.FDataType; - FDataType := ftFloat; - end - else if ( FLeft.FDataType in StringFieldTypes) and IsNumeric(FRight.FDataType )and - (FLeft.FKind = enConst) then - FLeft.FDataType := ftDateTime - else - DatabaseError(SExprTypeMis); - end; -end; - - -end. - - - +{$I pSQLDAC.inc} +{ *************************************************************************** } +{ } +{ Kylix and Delphi Cross-Platform Visual Component Library } +{ } +{ Copyright (c) 1995, 2001 Borland Software Corporation } +{ } +{ *************************************************************************** } + + +unit PSQLCommon; + + +{SVN revision: $Id$} + +{$T-,H+,X+,R-} + +interface + +uses Windows, Classes, + {$IFDEF DELPHI_6}Variants, SqlTimSt,{$ENDIF} + {$IFDEF FPC}Variants, {$ENDIF} + DB; + +type + TCANOperator = ( + coNOTDEFINED, { } + coISBLANK, { coUnary; is operand blank. } + coNOTBLANK, { coUnary; is operand not blank. } + coEQ, { coBinary, coCompare; equal. } + coNE, { coBinary; NOT equal. } + coGT, { coBinary; greater than. } + coLT, { coBinary; less than. } + coGE, { coBinary; greater or equal. } + coLE, { coBinary; less or equal. } + coNOT, { coUnary; NOT } + coAND, { coBinary; AND } + coOR, { coBinary; OR } + coTUPLE2, { coUnary; Entire record is operand. } + coFIELD2, { coUnary; operand is field } + coCONST2, { coUnary; operand is constant } + coMINUS, { coUnary; minus. } + coADD, { coBinary; addition. } + coSUB, { coBinary; subtraction. } + coMUL, { coBinary; multiplication. } + coDIV, { coBinary; division. } + coMOD, { coBinary; modulo division. } + coREM, { coBinary; remainder of division. } + coSUM, { coBinary, accumulate sum of. } + coCOUNT, { coBinary, accumulate count of. } + coMIN, { coBinary, find minimum of. } + coMAX, { coBinary, find maximum of. } + coAVG, { coBinary, find average of. } + coCONT, { coBinary; provides a link between two } + coUDF2, { coBinary; invokes a User defined fn } + coCONTINUE2, { coUnary; Stops evaluating records } + coLIKE, { coCompare, extended binary compare } + coIN, { coBinary field in list of values } + coLIST2, { List of constant values of same type } + coUPPER, { coUnary: upper case } + coLOWER, { coUnary: lower case } + coFUNC2, { coFunc: Function } + coLISTELEM2, { coListElem: List Element } + coASSIGN { coBinary: Field assignment } + ); + + NODEClass = ( { Node Class } + nodeNULL, { Null node } + nodeUNARY, { Node is a unary } + nodeBINARY, { Node is a binary } + nodeCOMPARE, { Node is a compare } + nodeFIELD, { Node is a field } + nodeCONST, { Node is a constant } + nodeTUPLE, { Node is a record } + nodeCONTINUE, { Node is a continue node } + nodeUDF, { Node is a UDF node } + nodeLIST, { Node is a LIST node } + nodeFUNC, { Node is a Function node } + nodeLISTELEM { Node is a List Element node } + ); + +const + CANEXPRSIZE = 10; { SizeOf(CANExpr) } + CANHDRSIZE = 8; { SizeOf(CANHdr) } + CANEXPRVERSION = 2; + + +type + TExprData = array of Byte; + TFieldMap = array[TFieldType] of Byte; + + {$IFDEF FPC} + TBlobByteData = array of Byte; + {$ENDIF} + +{ TFilterExpr } + +type + + TParserOption = (poExtSyntax, poAggregate, poDefaultExpr, poUseOrigNames, + poFieldNameGiven, poFieldDepend); + TParserOptions = set of TParserOption; + + TExprNodeKind = (enField, enConst, enOperator, enFunc); + TExprScopeKind = (skField, skAgg, skConst); + + PExprNode = ^TExprNode; + TExprNode = record + FNext: PExprNode; + FKind: TExprNodeKind; + FPartial: Boolean; + FOperator: TCANOperator; + FData: Variant; + FLeft: PExprNode; + FRight: PExprNode; + FDataType: TFieldType; + FDataSize: Integer; + FArgs: TList; + FScopeKind: TExprScopeKind; + end; + + TFilterExpr = class + private + FDataSet: TDataSet; + FFieldMap: TFieldMap; + FOptions: TFilterOptions; + FParserOptions: TParserOptions; + FNodes: PExprNode; + FExprBuffer: TExprData; + FExprBufSize: Integer; + FExprNodeSize: Integer; + FExprDataSize: Integer; + FFieldName: string; + FDependentFields: TBits; + function FieldFromNode(Node: PExprNode): TField; + function GetExprData(Pos, Size: Integer): PChar; +{$IFNDEF FPC} + function PutConstBCD(const Value: Variant; Decimals: Integer): Integer; +{$ENDIF} + function PutConstBool(const Value: Variant): Integer; + function PutConstDate(const Value: Variant): Integer; + function PutConstDateTime(const Value: Variant): Integer; + function PutConstFloat(const Value: Variant): Integer; + function PutConstInt(DataType: TFieldType; const Value: Variant): Integer; + +{$IFDEF DELPHI_6} + function PutConstSQLTimeStamp(const Value: Variant): Integer; + function PutConstInt64(DataType: TFieldType; const Value: Variant): Integer; + function PutConstFMTBCD(const Value: Variant; Decimals: Integer): Integer; +{$ENDIF} + + function PutConstNode(DataType: TFieldType; Data: PAnsiChar; + Size: Integer): Integer; + function PutConstStr(const Value: string): Integer; + function PutConstTime(const Value: Variant): Integer; + function PutData(Data: PAnsiChar; Size: Integer): Integer; + function PutExprNode(Node: PExprNode; ParentOp: TCANOperator): Integer; + function PutFieldNode(Field: TField; Node: PExprNode): Integer; + function PutNode(NodeType: NodeClass; OpType: TCANOperator; + OpCount: Integer): Integer; + procedure SetNodeOp(Node, Index, Data: Integer); + function PutConstant(Node: PExprNode): Integer; + function GetFieldByName(Name: string) : TField; + public + constructor Create(DataSet: TDataSet; Options: TFilterOptions; + ParseOptions: TParserOptions; const FieldName: string; DepFields: TBits; + FieldMap: TFieldMap); + destructor Destroy; override; + function NewCompareNode(Field: TField; Operator: TCANOperator; + const Value: Variant): PExprNode; + function NewNode(Kind: TExprNodeKind; Operator: TCANOperator; + const Data: Variant; Left, Right: PExprNode): PExprNode; + function GetFilterData(Root: PExprNode): TExprData; + property DataSet: TDataSet write FDataSet; + end; + +{ TExprParser } + + TExprToken = (etEnd, etSymbol, etName, etLiteral, etLParen, etRParen, + etEQ, etNE, etGE, etLE, etGT, etLT, etADD, etSUB, etMUL, etDIV, + etComma, etLIKE, etISNULL, etISNOTNULL, etIN); + + TExprParser = class + private + FDecimalSeparator: Char; + FFilter: TFilterExpr; + FFieldMap: TFieldMap; + FText: string; + FSourcePtr: PChar; + FTokenPtr: PChar; + FTokenString: string; + FStrTrue: string; + FStrFalse: string; + FToken: TExprToken; + FPrevToken: TExprToken; + FFilterData: TExprData; + FNumericLit: Boolean; + FDataSize: Integer; + FParserOptions: TParserOptions; + FFieldName: string; + FDataSet: TDataSet; + FDependentFields: TBits; + procedure NextToken; + function NextTokenIsLParen : Boolean; + function ParseExpr: PExprNode; + function ParseExpr2: PExprNode; + function ParseExpr3: PExprNode; + function ParseExpr4: PExprNode; + function ParseExpr5: PExprNode; + function ParseExpr6: PExprNode; + function ParseExpr7: PExprNode; + function TokenName: string; + function TokenSymbolIs(const S: string): Boolean; + function TokenSymbolIsFunc(const S: string) : Boolean; + procedure GetFuncResultInfo(Node: PExprNode); + procedure TypeCheckArithOp(Node: PExprNode); + procedure GetScopeKind(Root, Left, Right : PExprNode); + public + constructor Create(DataSet: TDataSet; const Text: string; + Options: TFilterOptions; ParserOptions: TParserOptions; + const FieldName: string; DepFields: TBits; FieldMap: TFieldMap); + destructor Destroy; override; + procedure SetExprParams(const Text: string; Options: TFilterOptions; + ParserOptions: TParserOptions; const FieldName: string); + property FilterData: TExprData read FFilterData; + property DataSize: Integer read FDataSize; + end; + +{ Field Origin parser } + +type + TFieldInfo = record + DatabaseName: string; + TableName: string; + OriginalFieldName: string; + end; + +function GetFieldInfo(const Origin: string; var FieldInfo: TFieldInfo): Boolean; + +{ SQL Parser } + +type + TSQLToken = (stUnknown, stTableName, stFieldName, stAscending, stDescending, stSelect, + stFrom, stWhere, stGroupBy, stHaving, stUnion, stPlan, stOrderBy, stForUpdate, + stEnd, stPredicate, stValue, stIsNull, stIsNotNull, stLike, stAnd, stOr, + stNumber, stAllFields, stComment, stDistinct); + +const + SQLSections = [stSelect, stFrom, stWhere, stGroupBy, stHaving, stUnion, + stPlan, stOrderBy, stForUpdate]; + +function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; +function GetIndexForOrderBy(const SQL: string; DataSet: TDataSet): TIndexDef; +function GetTableNameFromSQL(const SQL: string): string; +function GetTableNameFromQuery(const SQL: string): string; +function AddParamSQLForDetail(Params: TParams; SQL: string; Native: Boolean; QuoteChar: string = ''): string; +function IsMultiTableQuery(const SQL: string): Boolean; + +resourcestring + SInvalidFieldSize = 'Invalid field size'; + SInvalidFieldKind = 'Invalid FieldKind'; + SInvalidFieldRegistration = 'Invalid field registration'; + SUnknownFieldType = 'Field ''%s'' is of an unknown type'; + SFieldNameMissing = 'Field name missing'; + SDuplicateFieldName = 'Duplicate field name ''%s'''; + SFieldNotFound = 'Field ''%s'' not found'; + SFieldAccessError = 'Cannot access field ''%s'' as type %s'; + SFieldValueError = 'Invalid value for field ''%s'''; + SFieldRangeError = '%g is not a valid value for field ''%s''. The allowed range is %g to %g'; + SBcdFieldRangeError = '%s is not a valid value for field ''%s''. The allowed range is %s to %s'; + SInvalidIntegerValue = '''%s'' is not a valid integer value for field ''%s'''; + SInvalidBoolValue = '''%s'' is not a valid boolean value for field ''%s'''; + SInvalidFloatValue = '''%s'' is not a valid floating point value for field ''%s'''; + SFieldTypeMismatch = 'Type mismatch for field ''%s'', expecting: %s actual: %s'; + SFieldSizeMismatch = 'Size mismatch for field ''%s'', expecting: %d actual: %d'; + SInvalidVarByteArray = 'Invalid variant type or size for field ''%s'''; + SFieldOutOfRange = 'Value of field ''%s'' is out of range'; +// SBCDOverflow = '(Overflow)'; + SFieldRequired = 'Field ''%s'' must have a value'; + SDataSetMissing = 'Field ''%s'' has no dataset'; + SInvalidCalcType = 'Field ''%s'' cannot be a calculated or lookup field'; + SFieldReadOnly = 'Field ''%s'' cannot be modified'; + SFieldIndexError = 'Field index out of range'; + SNoFieldIndexes = 'No index currently active'; + SNotIndexField = 'Field ''%s'' is not indexed and cannot be modified'; + SIndexFieldMissing = 'Cannot access index field ''%s'''; + SDuplicateIndexName = 'Duplicate index name ''%s'''; + SNoIndexForFields = 'No index for fields ''%s'''; + SIndexNotFound = 'Index ''%s'' not found'; + SDuplicateName = 'Duplicate name ''%s'' in %s'; + SCircularDataLink = 'Circular datalinks are not allowed'; + SLookupInfoError = 'Lookup information for field ''%s'' is incomplete'; + SNewLookupFieldCaption = 'New Lookup Field'; + SDataSourceChange = 'DataSource cannot be changed'; + SNoNestedMasterSource = 'Nested datasets cannot have a MasterSource'; + SDataSetOpen = 'Cannot perform this operation on an open dataset'; + SNotEditing = 'Dataset not in edit or insert mode'; + SDataSetClosed = 'Cannot perform this operation on a closed dataset'; + SDataSetEmpty = 'Cannot perform this operation on an empty dataset'; + SDataSetReadOnly = 'Cannot modify a read-only dataset'; + SNestedDataSetClass = 'Nested dataset must inherit from %s'; + SExprTermination = 'Filter expression incorrectly terminated'; + SExprNameError = 'Unterminated field name'; + SExprStringError = 'Unterminated string constant'; + SExprInvalidChar = 'Invalid filter expression character: ''%s'''; + SExprNoLParen = '''('' expected but %s found'; + SExprNoRParen = ''')'' expected but %s found'; + SExprNoRParenOrComma = ''')'' or '','' expected but %s found'; + SExprExpected = 'Expression expected but %s found'; + SExprBadField = 'Field ''%s'' cannot be used in a filter expression'; + SExprBadNullTest = 'NULL only allowed with ''='' and ''<>'''; + SExprRangeError = 'Constant out of range'; + SExprNotBoolean = 'Field ''%s'' is not of type Boolean'; + SExprIncorrect = 'Incorrectly formed filter expression'; + SExprNothing = 'nothing'; + SExprTypeMis = 'Type mismatch in expression'; + SExprBadScope = 'Operation cannot mix aggregate value with record-varying value'; + SExprNoArith = 'Arithmetic in filter expressions not supported'; + SExprNotAgg = 'Expression is not an aggregate expression'; + SExprBadConst = 'Constant is not correct type %s'; + SExprNoAggFilter = 'Aggregate expressions not allowed in filters'; + SExprEmptyInList = 'IN predicate list may not be empty'; + SInvalidKeywordUse = 'Invalid use of keyword'; + STextFalse = 'False'; + STextTrue = 'True'; + SParameterNotFound = 'Parameter ''%s'' not found'; + SInvalidVersion = 'Unable to load bind parameters'; + SParamTooBig = 'Parameter ''%s'', cannot save data larger than %d bytes'; + SBadFieldType = 'Field ''%s'' is of an unsupported type'; + SAggActive = 'Property may not be modified while aggregate is active'; + SProviderSQLNotSupported = 'SQL not supported: %s'; + SProviderExecuteNotSupported = 'Execute not supported: %s'; + SExprNoAggOnCalcs = 'Field ''%s'' is not the correct type of calculated field to be used in an aggregate, use an internalcalc'; + SRecordChanged = 'Record not found or changed by another user'; + SDataSetUnidirectional = 'Operation not allowed on a unidirectional dataset'; + SUnassignedVar = 'Unassigned variant value'; + SRecordNotFound = 'Record not found'; + SFileNameBlank = 'FileName property cannot be blank'; + SFieldNameTooLarge = 'Fieldname %s exceeds %d chars'; + +{ For FMTBcd } + + SBcdOverflow = 'BCD overflow'; + SInvalidBcdValue = '%s is not a valid BCD value'; + SInvalidFormatType = 'Invalid format type for BCD'; + +{ For SqlTimSt } + + SCouldNotParseTimeStamp = 'Could not parse SQL TimeStamp string'; + SInvalidSqlTimeStamp = 'Invalid SQL date/time values'; + + SDeleteRecordQuestion = 'Delete record?'; + SDeleteMultipleRecordsQuestion = 'Delete all selected records?'; + STooManyColumns = 'Grid requested to display more than 256 columns'; + + { For reconcile error } + SSkip = 'Skip'; + SAbort = 'Abort'; + SMerge = 'Merge'; + SCorrect = 'Correct'; + SCancel = 'Cancel'; + SRefresh = 'Refresh'; + SModified = 'Modified'; + SInserted = 'Inserted'; + SDeleted = 'Deleted'; + SCaption = 'Update Error - %s'; + SUnchanged = ''; + SBinary = '(Binary)'; + SAdt = '(ADT)'; + SArray = '(Array)'; + SFieldName = 'Field Name'; + SOriginal = 'Original Value'; + SConflict = 'Conflicting Value'; + SValue = ' Value'; + SNoData = ''; + SNew = 'New'; + +implementation + +uses SysUtils, + {$IFNDEF FPC} + {$IFDEF DELPHI_6}FMTBcd,{$ENDIF} + {$ENDIF} + PSQLTypes; + +{ SQL Parser } + +function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; +var + DotStart: Boolean; + + function NextTokenIs(Value: string; var Str: string): Boolean; + var + Tmp: PChar; + S: string; + begin + Tmp := p; + NextSQLToken(Tmp, S, CurSection); + Result := AnsiCompareText(Value, S) = 0; + if Result then + begin + Str := Str + ' ' + S; + p := Tmp; + end; + end; + + function GetSQLToken(var Str: string): TSQLToken; + var + l: PChar; + s: string; + begin + if Length(Str) = 0 then + Result := stEnd else + if (Str = '*') and (CurSection = stSelect) then + Result := stAllFields else + if DotStart then + Result := stFieldName else + if (AnsiCompareText('DISTINCT', Str) = 0) and (CurSection = stSelect) then + Result := stDistinct else + if (AnsiCompareText('ASC', Str) = 0) or (AnsiCompareText('ASCENDING', Str) = 0)then + Result := stAscending else + if (AnsiCompareText('DESC', Str) = 0) or (AnsiCompareText('DESCENDING', Str) = 0)then + Result := stDescending else + if AnsiCompareText('SELECT', Str) = 0 then + Result := stSelect else + if AnsiCompareText('AND', Str) = 0 then + Result := stAnd else + if AnsiCompareText('OR', Str) = 0 then + Result := stOr else + if AnsiCompareText('LIKE', Str) = 0 then + Result := stLike else + if (AnsiCompareText('IS', Str) = 0) then + begin + if NextTokenIs('NULL', Str) then + Result := stIsNull else + begin + l := p; + s := Str; + if NextTokenIs('NOT', Str) and NextTokenIs('NULL', Str) then + Result := stIsNotNull else + begin + p := l; + Str := s; + Result := stValue; + end; + end; + end else + if AnsiCompareText('FROM', Str) = 0 then + Result := stFrom else + if AnsiCompareText('WHERE', Str) = 0 then + Result := stWhere else + if (AnsiCompareText('GROUP', Str) = 0) and NextTokenIs('BY', Str) then + Result := stGroupBy else + if AnsiCompareText('HAVING', Str) = 0 then + Result := stHaving else + if AnsiCompareText('UNION', Str) = 0 then + Result := stUnion else + if AnsiCompareText('PLAN', Str) = 0 then + Result := stPlan else + if (AnsiCompareText('FOR', Str) = 0) and NextTokenIs('UPDATE', Str) then + Result := stForUpdate else + if (AnsiCompareText('ORDER', Str) = 0) and NextTokenIs('BY', Str) then + Result := stOrderBy else + if AnsiCompareText('NULL', Str) = 0 then + Result := stValue else + if CurSection = stFrom then + Result := stTableName else + Result := stFieldName; + end; + +var + TokenStart: PChar; + + procedure StartToken; + begin + if not Assigned(TokenStart) then + TokenStart := p; + end; + +var + Literal: Char; + Mark: PChar; +begin + TokenStart := nil; + DotStart := False; + while True do + begin + case p^ of + '"','''','`': + begin + StartToken; + Literal := p^; + Mark := p; + repeat Inc(p) until CharInSet(p^, [Literal,#0]); + if p^ = #0 then + begin + p := Mark; + Inc(p); + end else + begin + Inc(p); + SetString(Token, TokenStart, p - TokenStart); + Mark := PChar(Token); + Token := AnsiExtractQuotedStr(Mark, Literal); + if DotStart then + Result := stFieldName else + if p^ = '.' then + Result := stTableName else + Result := stValue; + Exit; + end; + end; + '/': + begin + StartToken; + Inc(p); + if CharInSet(p^, ['/','*']) then + begin + if p^ = '*' then + begin + repeat Inc(p) until (p = #0) or ((p^ = '*') and (p[1] = '/')); + end else + while not CharInSet(p^, [#0, #10, #13]) do Inc(p); + SetString(Token, TokenStart, p - TokenStart); + Result := stComment; + Exit; + end; + end; + ' ', #10, #13, ',', '(': + begin + if Assigned(TokenStart) then + begin + SetString(Token, TokenStart, p - TokenStart); + Result := GetSQLToken(Token); + Exit; + end else + while CharInSet(p^, [' ', #10, #13, ',', '(']) do Inc(p); + end; + '.': + begin + if Assigned(TokenStart) then + begin + SetString(Token, TokenStart, p - TokenStart); + Result := stTableName; + Exit; + end else + begin + DotStart := True; + Inc(p); + end; + end; + '=','<','>': + begin + if not Assigned(TokenStart) then + begin + TokenStart := p; + while CharInSet(p^, ['=','<','>']) do Inc(p); + SetString(Token, TokenStart, p - TokenStart); + Result := stPredicate; + Exit; + end; + Inc(p); + end; + '0'..'9': + begin + if not Assigned(TokenStart) then + begin + TokenStart := p; + while CharInSet(p^, ['0'..'9','.']) do Inc(p); + SetString(Token, TokenStart, p - TokenStart); + Result := stNumber; + Exit; + end else + Inc(p); + end; + #0: + begin + if Assigned(TokenStart) then + begin + SetString(Token, TokenStart, p - TokenStart); + Result := GetSQLToken(Token); + Exit; + end else + begin + Result := stEnd; + Token := ''; + Exit; + end; + end; + else + StartToken; + Inc(p); + end; + end; +end; + +function AddParamSQLForDetail(Params: TParams; SQL: string; Native: Boolean; QuoteChar: string = ''): string; +const + SWhere = ' where '; { do not localize } + SAnd = ' and '; { do not localize } + + function GenerateParamSQL: string; + var + I: Integer; + ParamName: string; + begin + for I := 0 to Params.Count -1 do + begin + if QuoteChar = '"' then + ParamName := '"' + StringReplace(Params[I].Name, '"', '""', [rfReplaceAll] ) + '"' + else + ParamName := QuoteChar + Params[I].Name +QuoteChar; + if I > 0 then Result := Result + SAnd; + if Native then + Result := Result + format('%s = ?', [ParamName]) + else + Result := Result + format('%s = :%s', [ParamName, ParamName]); + end; + if pos(SWhere, LowerCase(Result)) > 0 then + Result := SAnd + Result + else + Result := SWhere + Result; + end; + + function AddWhereClause: string; + var + Start: PChar; + Rest, FName: string; + SQLToken, CurSection: TSQLToken; + begin + Start := PChar(SQL); + CurSection := stUnknown; + repeat + SQLToken := NextSQLToken(Start, FName, CurSection); + until SQLToken in [stFrom, stEnd]; + if SQLToken = stFrom then + NextSQLToken(Start, FName, CurSection); + Rest := string(Start); + if Rest = '' then + Result := SQL + ' ' + GenerateParamSQL + else + Result := Copy(SQL, 1, pos(Rest, SQL)) + ' ' + GenerateParamSQL + Rest; + end; + +begin + Result := SQL; + if (Params.Count > 0) then + Result := AddWhereClause; +end; + +// SQL might be a direct tablename; +function GetTableNameFromQuery(const SQL: string): string; +begin + if pos( 'select', lowercase(SQL) ) < 1 then + Result := SQL + else + Result := GetTableNameFromSQL(SQL); +end; + +function GetTableNameFromSQL(const SQL: string): string; +var + Start: PChar; + Token: string; + SQLToken, CurSection: TSQLToken; +begin + Result := ''; + Start := PChar(SQL); + CurSection := stUnknown; + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then CurSection := SQLToken; + until SQLToken in [stEnd, stFrom]; + if SQLToken = stFrom then + begin + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then + CurSection := SQLToken else + // stValue is returned if TableNames contain quote chars. + if (SQLToken = stTableName) or (SQLToken = stValue) then + begin + Result := Token; + while (Start[0] = '.') and not (SQLToken in [stEnd]) do + begin + SQLToken := NextSqlToken(Start, Token, CurSection); + Result := Result + '.' + Token; + end; + Exit; + end; + until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]); + end; +end; + +function IsMultiTableQuery(const SQL: string): Boolean; +const + SInnerJoin = 'inner join '; { do not localize } + SOuterJoin = 'outer join '; { do not localize } +var + Start: PChar; + SResult, Token: string; + SQLToken, CurSection: TSQLToken; +begin + SResult := ''; + Start := PChar(SQL); + CurSection := stUnknown; + Result := True; + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then CurSection := SQLToken; + until SQLToken in [stEnd, stFrom]; + if SQLToken = stFrom then + begin + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then + CurSection := SQLToken else + // stValue is returned if TableNames contain quote chars. + if (SQLToken = stTableName) or (SQLToken = stValue) then + begin + SResult := Token; + while (Start[0] = '.') and not (SQLToken in [stEnd]) do + begin + SQLToken := NextSqlToken(Start, Token, CurSection); + SResult := SResult + '.' + Token; + end; + if (Start[0] = ',') or (Start[1] = ',') then + exit; + NextSqlToken(Start, Token, CurSection); + if Assigned(AnsiStrPos(Start, PChar(SInnerJoin))) or + Assigned(AnsiStrPos(Start, PChar(SOuterJoin))) then + Exit; + SQLToken := NextSqlToken(Start, Token, CurSection); + if SQLToken = stTableName then + Exit; + Result := False; + Exit; + end; + until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]); + end; +end; + +function GetIndexForOrderBy(const SQL: string; DataSet: TDataSet): TIndexDef; + + function AddField(const Fields, NewField: string): string; + begin + Result := Fields; + if Fields <> '' then + Result := Fields + ';' + NewField else + Result := NewField; + end; + +var + Start: PChar; + Token, LastField, SaveField: string; + SQLToken, CurSection: TSQLToken; + FieldIndex: Integer; +begin + Result := nil; + Start := PChar(SQL); + CurSection := stUnknown; + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then CurSection := SQLToken; + until SQLToken in [stEnd, stOrderBy]; + if SQLToken = stOrderBy then + begin + Result := TIndexDef.Create(nil); + try + LastField := ''; + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then + CurSection := SQLToken else + case SQLToken of + stTableName: ; + stFieldName: + begin + LastField := Token; + { Verify that we parsed a valid field name, not something like "UPPER(Foo)" } + if not Assigned(Dataset.FindField(LastField)) then continue; + Result.Fields := AddField(Result.Fields, LastField); + SaveField := LastField; + end; + stAscending: ; + stDescending: + Result.DescFields := AddField(Result.DescFields, SaveField); + stNumber: + begin + FieldIndex := StrToInt(Token); + if DataSet.FieldCount >= FieldIndex then + LastField := DataSet.Fields[FieldIndex - 1].FieldName else + if DataSet.FieldDefs.Count >= FieldIndex then + LastField := DataSet.FieldDefs[FieldIndex - 1].Name + else + { DB2 specific syntax "FETCH FIRST n ROWS ONLY" is blocked here, + so commenting out the following line } + //SysUtils.Abort; + continue; + Result.Fields := AddField(Result.Fields, LastField); + end; + end; + until (CurSection <> stOrderBy) or (SQLToken = stEnd); + finally + if Result.Fields = '' then + begin + Result.Free; + Result := nil; + end; + end; + end; +end; + +function GetFieldInfo(const Origin: string; var FieldInfo: TFieldInfo): Boolean; +var + Current: PChar; + Values: array[0..4] of string; + I: Integer; + + function GetPChar(const S: string): PChar; + begin + if S <> '' then Result := PChar(Pointer(S)) else Result := ''; + end; + + procedure Split(const S: string); + begin + Current := PChar(Pointer(S)); + end; + + function NextItem: string; + var + C: PChar; + I: PChar; + Terminator: Char; + Ident: array[0..1023] of Char; + begin + Result := ''; + C := Current; + I := Ident; + while CharInSet(C^, ['.',' ',#0]) do + if C^ = #0 then Exit else Inc(C); + Terminator := '.'; + if C^ = '"' then + begin + Terminator := '"'; + Inc(C); + end; + while not CharInSet(C^, [Terminator, #0]) do + begin + if CharInSet(C^, LeadBytes) then + begin + I^ := C^; + Inc(C); + Inc(I); + end + else if C^ = '\' then + begin + Inc(C); + if CharInSet(C^, LeadBytes) then + begin + I^ := C^; + Inc(C); + Inc(I); + end; + if C^ = #0 then Dec(C); + end; + I^ := C^; + Inc(C); + Inc(I); + end; + SetString(Result, Ident, I - Ident); + if (Terminator = '"') and (C^ <> #0) then Inc(C); + Current := C; + end; + + function PopValue: PChar; + begin + if I >= 0 then + begin + Result := GetPChar(Values[I]); + Dec(I); + end else Result := ''; + end; + +begin + Result := False; + if (Origin = '') then Exit; + Split(Origin); + I := -1; + repeat + Inc(I); + Values[I] := NextItem; + until (Values[I] = '') or (I = High(Values)); + if I = High(Values) then Exit; + Dec(I); + FieldInfo.OriginalFieldName := StrPas(PopValue); + FieldInfo.TableName := StrPas(PopValue); + FieldInfo.DatabaseName := StrPas(PopValue); + Result := (FieldInfo.OriginalFieldName <> '') and (FieldInfo.TableName <> ''); +end; + +const + StringFieldTypes = [ftString, ftFixedChar, ftWideString, ftGuid]; + BlobFieldTypes = [ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, + ftTypedBinary, ftOraBlob, ftOraClob]; + +function IsNumeric(DataType: TFieldType): Boolean; +begin + Result := DataType in [ftSmallint, ftInteger, ftWord, ftFloat, ftCurrency, + ftBCD, ftAutoInc, ftLargeint{$IFDEF DELPHI_6}, ftFMTBcd{$ENDIF}]; +end; + +function IsTemporal(DataType: TFieldType): Boolean; +begin + Result := DataType in [ftDate, ftTime, ftDateTime{$IFDEF DELPHI_6}, ftTimeStamp{$ENDIF}]; +end; + +{ TFilterExpr } + +constructor TFilterExpr.Create(DataSet: TDataSet; Options: TFilterOptions; + ParseOptions: TParserOptions; const FieldName: string; DepFields: TBits; + FieldMap: TFieldMap); +begin + FFieldMap := FieldMap; + FDataSet := DataSet; + FOptions := Options; + FFieldName := FieldName; + FParserOptions := ParseOptions; + FDependentFields := DepFields; +end; + +destructor TFilterExpr.Destroy; +var + Node: PExprNode; +begin + SetLength(FExprBuffer, 0); + while FNodes <> nil do + begin + Node := FNodes; + FNodes := Node^.FNext; + if (Node^.FKind = enFunc) and (Node^.FArgs <> nil) then + Node^.FArgs.Free; + Dispose(Node); + end; +end; + +function TFilterExpr.FieldFromNode(Node: PExprNode): TField; +begin + Result := GetFieldByName(Node^.FData); + if not (Result.FieldKind in [fkData, fkInternalCalc]) then + DatabaseErrorFmt(SExprBadField, [Result.FieldName]); +end; + +function TFilterExpr.GetExprData(Pos, Size: Integer): PChar; +begin + SetLength(FExprBuffer, FExprBufSize + Size); + Move(FExprBuffer[Pos], FExprBuffer[Pos + Size], FExprBufSize - Pos); + Inc(FExprBufSize, Size); + Result := PChar(FExprBuffer) + Pos; +end; + +function TFilterExpr.GetFilterData(Root: PExprNode): TExprData; +begin + FExprBufSize := CANExprSize; + SetLength(FExprBuffer, FExprBufSize); + PutExprNode(Root, coNOTDEFINED); + PWord(@FExprBuffer[0])^ := CANEXPRVERSION; { iVer } + PWord(@FExprBuffer[2])^ := FExprBufSize; { iTotalSize } + PWord(@FExprBuffer[4])^ := $FFFF; { iNodes } + PWord(@FExprBuffer[6])^ := CANEXPRSIZE; { iNodeStart } + PWord(@FExprBuffer[8])^ := FExprNodeSize + CANEXPRSIZE; { iLiteralStart } + Result := FExprBuffer; +end; + +function TFilterExpr.NewCompareNode(Field: TField; Operator: TCANOperator; + const Value: Variant): PExprNode; +var + ConstExpr: PExprNode; +begin + ConstExpr := NewNode(enConst, coNOTDEFINED, Value, nil, nil); + ConstExpr^.FDataType := Field.DataType; + ConstExpr^.FDataSize := Field.Size; + Result := NewNode(enOperator, Operator, Unassigned, + NewNode(enField, coNOTDEFINED, Field.FieldName, nil, nil), ConstExpr); +end; + +function TFilterExpr.NewNode(Kind: TExprNodeKind; Operator: TCANOperator; + const Data: Variant; Left, Right: PExprNode): PExprNode; +var + Field : TField; +begin + New(Result); + with Result^ do + begin + FNext := FNodes; + FKind := Kind; + FPartial := False; + FOperator := Operator; + FData := Data; + FLeft := Left; + FRight := Right; + end; + FNodes := Result; + if Kind = enField then + begin + Field := GetFieldByName(Data); + if Field = nil then + DatabaseErrorFmt(SFieldNotFound, [Data]); + Result^.FDataType := Field.DataType; + Result^.FDataSize := Field.Size; + end; +end; + +{$IFNDEF FPC} +function TFilterExpr.PutConstBCD(const Value: Variant; + Decimals: Integer): Integer; +var + C: Currency; + BCD: TBcd; +begin + if VarType(Value) = varString then + C := StrToCurr(string(TVarData(Value).VString)) else + C := Value; + CurrToBCD(C, BCD, 32, Decimals); + Result := PutConstNode(ftBCD, @BCD, 18); +end; +{$ENDIF} + +{$IFDEF DELPHI_6} +function TFilterExpr.PutConstFMTBCD(const Value: Variant; + Decimals: Integer): Integer; +var + BCD: TBcd; +begin + + if VarType(Value) = varString then + BCD := StrToBcd(string(TVarData(Value).VString)) else + BCD := VarToBcd(Value); + Result := PutConstNode(ftBCD, @BCD, 18); +end; + +function TFilterExpr.PutConstSQLTimeStamp(const Value: Variant): Integer; +var + TimeStamp: TSQLTimeStamp; +begin + if VarType(Value) = varString then + TimeStamp := StrToSQLTimeStamp(string(TVarData(Value).VString)) else + TimeStamp := VarToSQLTimeStamp(Value); + Result := PutConstNode(ftTimeStamp, @TimeStamp, 16); +end; + +function TFilterExpr.PutConstInt64(DataType: TFieldType; + const Value: Variant): Integer; +var + IntValue: LargeInt; +begin + IntValue := Value; + Result := PutConstNode(DataType, @IntValue, SizeOf(IntValue)); +end; +{$ENDIF} + +function TFilterExpr.PutConstBool(const Value: Variant): Integer; +var + B: WordBool; +begin + B := Value; + Result := PutConstNode(ftBoolean, @B, SizeOf(WordBool)); +end; + +function TFilterExpr.PutConstDate(const Value: Variant): Integer; +var + DateTime: TDateTime; + TimeStamp: TTimeStamp; +begin + if VarType(Value) = varString then + DateTime := StrToDate(string(TVarData(Value).VString)) else + DateTime := VarToDateTime(Value); + TimeStamp := DateTimeToTimeStamp(DateTime); + Result := PutConstNode(ftDate, @TimeStamp.Date, 4); +end; + +function TFilterExpr.PutConstDateTime(const Value: Variant): Integer; +var + DateTime: TDateTime; + DateData: Double; +begin + if VarType(Value) = varString then + {$IFDEF DELPHI_7} + begin + if not TryStrToDate(string(TVarData(Value).VString), DateTime) then + DateTime := VarToDateTime(Value); + end + {$ELSE} + try + DateTime := StrToDateTime(string(TVarData(Value).VString)) + except + DateTime := VarToDateTime(Value) + end + {$ENDIF UNDER_DELPHI_6} + else + DateTime := VarToDateTime(Value); + DateData := TimeStampToMSecs(DateTimeToTimeStamp(DateTime)); + Result := PutConstNode(ftDateTime, @DateData, 8); +end; + +function TFilterExpr.PutConstFloat(const Value: Variant): Integer; +var + F: Double; +begin + if VarType(Value) = varString then + F := StrToFloat(string(TVarData(Value).VString)) else + F := Value; + Result := PutConstNode(ftFloat, @F, SizeOf(Double)); +end; + +function TFilterExpr.PutConstInt(DataType: TFieldType; + const Value: Variant): Integer; +var + I, Size: Integer; +begin + if VarType(Value) = varString then + I := StrToInt(string(TVarData(Value).VString)) else + I := Value; + Size := 2; + case DataType of + ftSmallint: + if (I < -32768) or (I > 32767) then DatabaseError(SExprRangeError); + ftWord: + if (I < 0) or (I > 65535) then DatabaseError(SExprRangeError); + else + Size := 4; + end; + Result := PutConstNode(DataType, @I, Size); +end; + +function TFilterExpr.PutConstNode(DataType: TFieldType; Data: PAnsiChar; + Size: Integer): Integer; +begin + Result := PutNode(nodeCONST, coCONST2, 3); + SetNodeOp(Result, 0, FFieldMap[DataType]); + SetNodeOp(Result, 1, Size); + SetNodeOp(Result, 2, PutData(Data, Size)); +end; + +function TFilterExpr.PutConstStr(const Value: string): Integer; +var + Str: string; + Buffer: array[0..255] of AnsiChar; +begin + if Length(Value) >= SizeOf(Buffer) then + Str := Copy(Value, 1, SizeOf(Buffer) - 1) else + Str := Value; + FDataSet.Translate(PAnsiChar(AnsiString(Str)), Buffer, True); + Result := PutConstNode(ftString, Buffer, Length(Str) + 1); +end; + +function TFilterExpr.PutConstTime(const Value: Variant): Integer; +var + DateTime: TDateTime; + TimeStamp: TTimeStamp; +begin + if VarType(Value) = varString then + DateTime := StrToTime(string(TVarData(Value).VString)) else + DateTime := VarToDateTime(Value); + TimeStamp := DateTimeToTimeStamp(DateTime); + Result := PutConstNode(ftTime, @TimeStamp.Time, 4); +end; + +function TFilterExpr.PutData(Data: PAnsiChar; Size: Integer): Integer; +begin + Move(Data^, GetExprData(FExprBufSize, Size)^, Size); + Result := FExprDataSize; + Inc(FExprDataSize, Size); +end; + +function TFilterExpr.PutConstant(Node: PExprNode): Integer; +begin + Result := 0; + case Node^.FDataType of + ftSmallInt, ftInteger, ftWord, ftAutoInc: + Result := PutConstInt(Node^.FDataType, Node^.FData); + ftFloat, ftCurrency: + Result := PutConstFloat(Node^.FData); + ftString, ftWideString, ftFixedChar, ftGuid: + Result := PutConstStr(Node^.FData); + ftDate: + Result := PutConstDate(Node^.FData); + ftTime: + Result := PutConstTime(Node^.FData); + ftDateTime: + Result := PutConstDateTime(Node^.FData); + ftBoolean: + Result := PutConstBool(Node^.FData); +{$IFDEF DELPHI_6} + ftTimeStamp: + Result := PutConstSQLTimeStamp(Node^.FData); + ftFMTBcd: + Result := PutConstFMTBCD(Node^.FData, Node^.FDataSize); + ftLargeint: + Result := PutConstInt64(Node^.FDataType, Node^.FData); +{$ENDIF} +{$IFNDEF FPC} + ftBCD: + Result := PutConstBCD(Node^.FData, Node^.FDataSize); +{$ENDIF} + else + DatabaseErrorFmt(SExprBadConst, [Node^.FData]); + end; +end; + + +function TFilterExpr.PutExprNode(Node: PExprNode; ParentOp: TCANOperator): Integer; +const + ReverseOperator: array[coEQ..coLE] of TCANOperator = (coEQ, coNE, coLT, + coGT, coLE, coGE); + BoolFalse: WordBool = False; +var + Field: TField; + Left, Right, Temp : PExprNode; + LeftPos, RightPos, ListElem, PrevListElem, I: Integer; + Operator: TCANOperator; + CaseInsensitive, PartialLength, L: Integer; + S: string; +begin + Result := 0; + case Node^.FKind of + enField: + begin + Field := FieldFromNode(Node); + if (ParentOp in [coOR, coNOT, coAND, coNOTDEFINED]) and + (Field.DataType = ftBoolean) then + begin + Result := PutNode(nodeBINARY, coNE, 2); + SetNodeOp(Result, 0, PutFieldNode(Field, Node)); + SetNodeOp(Result, 1, PutConstNode(ftBoolean, @BoolFalse, SizeOf(WordBool))); + end + else + Result := PutFieldNode(Field, Node); + end; + enConst: + Result := PutConstant(Node); + enOperator: + case Node^.FOperator of + coIN: + begin + Result := PutNode(nodeBINARY, coIN, 2); + SetNodeOp(Result, 0, PutExprNode(Node^.FLeft,Node^.FOperator)); + ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); + SetNodeOp(Result, 1, ListElem); + PrevListElem := ListElem; + for I := 0 to Node^.FArgs.Count - 1 do + begin + LeftPos := PutExprNode(Node^.FArgs.Items[I],Node^.FOperator); + if I = 0 then + begin + SetNodeOp(PrevListElem, 0, LeftPos); + SetNodeOp(PrevListElem, 1, 0); + end + else + begin + ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); + SetNodeOp(ListElem, 0, LeftPos); + SetNodeOp(ListElem, 1, 0); + SetNodeOp(PrevListElem, 1, ListElem); + PrevListElem := ListElem; + end; + end; + end; + coNOT, + coISBLANK, + coNOTBLANK: + begin + Result := PutNode(nodeUNARY, Node^.FOperator, 1); + SetNodeOp(Result, 0, PutExprNode(Node^.FLeft,Node^.FOperator)); + end; + coEQ..coLE, + coAND,coOR, + coADD..coDIV, + coLIKE, + coASSIGN: + begin + Operator := Node^.FOperator; + Left := Node^.FLeft; + Right := Node^.FRight; + if (Operator in [coEQ..coLE]) and (Right^.FKind = enField) and + (Left^.FKind <> enField) then + begin + Temp := Left; + Left := Right; + Right := Temp; + Operator := ReverseOperator[Operator]; + end; + + Result := 0; + if (Left^.FKind = enField) and (Right^.FKind = enConst) + and ((Node^.FOperator = coEQ) or (Node^.FOperator = coNE) + or (Node^.FOperator = coLIKE)) then + begin + if VarIsNull(Right^.FData) then + begin + case Node^.FOperator of + coEQ: Operator := coISBLANK; + coNE: Operator := coNOTBLANK; + else + DatabaseError(SExprBadNullTest); + end; + Result := PutNode(nodeUNARY, Operator, 1); + SetNodeOp(Result, 0, PutExprNode(Left,Node^.FOperator)); + end + else if (Right^.FDataType in StringFieldTypes) then + begin + S := Right^.FData; + L := Length(S); + if L <> 0 then + begin + CaseInsensitive := 0; + PartialLength := 0; + if foCaseInsensitive in FOptions then CaseInsensitive := 1; + if Node^.FPartial then PartialLength := L else + if not (foNoPartialCompare in FOptions) and (L > 1) and + (S[L] = '*') then + begin + Delete(S, L, 1); + PartialLength := L - 1; + end; + if (CaseInsensitive <> 0) or (PartialLength <> 0) then + begin + Result := PutNode(nodeCOMPARE, Operator, 4); + SetNodeOp(Result, 0, CaseInsensitive); + SetNodeOp(Result, 1, PartialLength); + SetNodeOp(Result, 2, PutExprNode(Left,Node^.FOperator)); + SetNodeOp(Result, 3, PutConstStr(S)); + end; + end; + end; + end; + + if Result = 0 then + begin + if (Operator = coISBLANK) or (Operator = coNOTBLANK) then + begin + Result := PutNode(nodeUNARY, Operator, 1); + LeftPos := PutExprNode(Left,Node^.FOperator); + SetNodeOp(Result, 0, LeftPos); + end else + begin + Result := PutNode(nodeBINARY, Operator, 2); + LeftPos := PutExprNode(Left,Node^.FOperator); + RightPos := PutExprNode(Right,Node^.FOperator); + SetNodeOp(Result, 0, LeftPos); + SetNodeOp(Result, 1, RightPos); + end; + end; + end; + end; + enFunc: + begin + Result := PutNode(nodeFUNC, coFUNC2, 2); + SetNodeOp(Result, 0, PutData(PAnsiChar(ansistring(Node^.FData)), + Length(string(Node^.FData)) + 1)); + if Node^.FArgs <> nil then + begin + ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); + SetNodeOp(Result, 1, ListElem); + PrevListElem := ListElem; + for I := 0 to Node^.FArgs.Count - 1 do + begin + LeftPos := PutExprNode(Node^.FArgs.Items[I],Node^.FOperator); + if I = 0 then + begin + SetNodeOp(PrevListElem, 0, LeftPos); + SetNodeOp(PrevListElem, 1, 0); + end + else + begin + ListElem := PutNode(nodeLISTELEM, coLISTELEM2, 2); + SetNodeOp(ListElem, 0, LeftPos); + SetNodeOp(ListElem, 1, 0); + SetNodeOp(PrevListElem, 1, ListElem); + PrevListElem := ListElem; + end; + end; + end else + SetNodeOp(Result, 1, 0); + end; + end; +end; + + +function TFilterExpr.PutFieldNode(Field: TField; Node: PExprNode): Integer; +var + Buffer: array[0..255] of ansiChar; +begin + if poFieldNameGiven in FParserOptions then + FDataSet.Translate(PAnsiChar(Ansistring(Field.FieldName)), Buffer, True) + else + FDataSet.Translate(PAnsiChar(Ansistring(Node^.FData)), Buffer, True); + Result := PutNode(nodeFIELD, coFIELD2, 2); + SetNodeOp(Result, 0, Field.FieldNo); + SetNodeOp(Result, 1, PutData(Buffer, StrLen(Buffer) + 1)); +end; + +function TFilterExpr.PutNode(NodeType: NodeClass; OpType: TCANOperator; + OpCount: Integer): Integer; +var + Size: Integer; + Data: PChar; +begin + Size := CANHDRSIZE + OpCount * SizeOf(Word); + Data := GetExprData(CANEXPRSIZE + FExprNodeSize, Size); + PInteger(@Data[0])^ := Integer(NodeType); { CANHdr.nodeClass } + PInteger(@Data[4])^ := Integer(OpType); { CANHdr.coOp } + Result := FExprNodeSize; + Inc(FExprNodeSize, Size); +end; + +procedure TFilterExpr.SetNodeOp(Node, Index, Data: Integer); +begin + PWordArray(PChar(FExprBuffer) + (CANEXPRSIZE + Node + + CANHDRSIZE))^[Index] := Data; +end; + +function TFilterExpr.GetFieldByName(Name: string) : TField; +var + I: Integer; + F: TField; + FieldInfo: TFieldInfo; +begin + Result := nil; + if poFieldNameGiven in FParserOptions then + Result := FDataSet.FieldByName(FFieldName) + else if poUseOrigNames in FParserOptions then + begin + for I := 0 to FDataset.FieldCount - 1 do + begin + F := FDataSet.Fields[I]; + if GetFieldInfo(F.Origin, FieldInfo) and + (AnsiCompareStr(Name, FieldInfo.OriginalFieldName) = 0) then + begin + Result := F; + Exit; + end; + end; + end; + if Result = nil then + Result := FDataSet.FieldByName(Name); + if (Result <> nil) and (Result.FieldKind = fkCalculated) and (poAggregate in FParserOptions) then + DatabaseErrorFmt(SExprNoAggOnCalcs, [Result.FieldName]); + if (poFieldDepend in FParserOptions) and (Result <> nil) and + (FDependentFields <> nil) then + FDependentFields[Result.FieldNo-1] := True; +end; + +constructor TExprParser.Create(DataSet: TDataSet; const Text: string; + Options: TFilterOptions; ParserOptions: TParserOptions; const FieldName: string; + DepFields: TBits; FieldMap: TFieldMap); +begin + FDecimalSeparator := PSQLTypes.PSQL_FS.DecimalSeparator; + FFieldMap := FieldMap; + FStrTrue := STextTrue; + FStrFalse := STextFalse; + FDataSet := DataSet; + FDependentFields := DepFields; + FFilter := TFilterExpr.Create(DataSet, Options, ParserOptions, FieldName, + DepFields, FieldMap); + if Text <> '' then + SetExprParams(Text, Options, ParserOptions, FieldName); +end; + +destructor TExprParser.Destroy; +begin + FFilter.Free; +end; + +procedure TExprParser.SetExprParams(const Text: string; Options: TFilterOptions; + ParserOptions: TParserOptions; const FieldName: string); +var + Root, DefField: PExprNode; +begin + FParserOptions := ParserOptions; + if FFilter <> nil then + FFilter.Free; + FFilter := TFilterExpr.Create(FDataSet, Options, ParserOptions, FieldName, + FDependentFields, FFieldMap); + FText := Text; + FSourcePtr := PChar(Text); + FFieldName := FieldName; + NextToken; + Root := ParseExpr; + if FToken <> etEnd then DatabaseError(SExprTermination); + if (poAggregate in FParserOptions) and (Root^.FScopeKind <> skAgg) then + DatabaseError(SExprNotAgg); + if (not (poAggregate in FParserOptions)) and (Root^.FScopeKind = skAgg) then + DatabaseError(SExprNoAggFilter); + if poDefaultExpr in ParserOptions then + begin + DefField := FFilter.NewNode(enField, coNOTDEFINED, FFieldName, nil, nil); + if (IsTemporal(DefField^.FDataType) and (Root^.FDataType in StringFieldTypes)) or + ((DefField^.FDataType = ftBoolean ) and (Root^.FDataType in StringFieldTypes)) then + Root^.FDataType := DefField^.FDataType; + + if not ((IsTemporal(DefField^.FDataType) and IsTemporal(Root^.FDataType)) + or (IsNumeric(DefField^.FDataType) and IsNumeric(Root^.FDataType)) + or ((DefField^.FDataType in StringFieldTypes) and (Root^.FDataType in StringFieldTypes)) + or ((DefField^.FDataType = ftBoolean) and (Root^.FDataType = ftBoolean))) then + DatabaseError(SExprTypeMis); + Root := FFilter.NewNode(enOperator, coASSIGN, Unassigned, Root, DefField); + end; + + if not (poAggregate in FParserOptions) and not(poDefaultExpr in ParserOptions) + and (Root^.FDataType <> ftBoolean ) then + DatabaseError(SExprIncorrect); + + FFilterData := FFilter.GetFilterData(Root); + FDataSize := FFilter.FExprBufSize; +end; + +function TExprParser.NextTokenIsLParen : Boolean; +var + P : PChar; +begin + P := FSourcePtr; + while (P^ <> #0) and (P^ <= ' ') do Inc(P); + Result := P^ = '('; +end; + +function EndOfLiteral(var P : PChar): Boolean; +var + FName: String; + PTemp: PChar; +begin + Inc(P); + Result := P^ <> ''''; + if Result then + begin // now, look for 'John's Horse' + if AnsiStrScan(P, '''') <> Nil then // found another ' + begin + PTemp := P; // don't advance P + while CharInSet(PTemp[0], [ ' ', ')' ]) do Inc(PTemp); + if NextSQLToken(PTemp, FName, stValue) in [stFieldName, stUnknown] then + begin // 'John's Horse' case: not really end of literal + Result := False; + Dec(P); + end; + end; + end; +end; + +procedure TExprParser.NextToken; +type + ASet = Set of AnsiChar; +var + P, TokenStart: PChar; + L: Integer; + StrBuf: array[0..255] of Char; + + function IsKatakana(const Chr: Byte): Boolean; + begin + Result := (SysLocale.PriLangID = LANG_JAPANESE) and (Chr in [$A1..$DF]); + end; + + procedure Skip(TheSet: ASet); + begin + while TRUE do + begin + if CharInSet(P^, LeadBytes) then + Inc(P, 2) + else if CharInSet(P^, TheSet) or IsKatakana(Byte(P^)) then + Inc(P) + else + Exit; + end; + end; + +begin + FPrevToken := FToken; + FTokenString := ''; + P := FSourcePtr; + while (P^ <> #0) and (P^ <= ' ') do Inc(P); + if (P^ <> #0) and (P^ = '/') and (P[1] <> #0) and (P[1] = '*')then + begin + P := P + 2; + while (P^ <> #0) and (P^ <> '*') do Inc(P); + if (P^ = '*') and (P[1] <> #0) and (P[1] = '/') then + P := P + 2 + else + DatabaseErrorFmt(SExprInvalidChar, [P^]); + end; + while (P^ <> #0) and (P^ <= ' ') do Inc(P); + FTokenPtr := P; + case P^ of + 'A'..'Z', 'a'..'z', '_', #$81..#$fe: + begin + TokenStart := P; + if not SysLocale.FarEast then + begin + Inc(P); + while CharInSet(P^, ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '[', ']']) do Inc(P); + end + else + Skip(['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '[', ']']); + SetString(FTokenString, TokenStart, P - TokenStart); + FToken := etSymbol; + if CompareText(FTokenString, 'LIKE') = 0 then { do not localize } + FToken := etLIKE + else if CompareText(FTokenString, 'IN') = 0 then { do not localize } + FToken := etIN + else if CompareText(FTokenString, 'IS') = 0 then { do not localize } + begin + while (P^ <> #0) and (P^ <= ' ') do Inc(P); + TokenStart := P; + Skip(['A'..'Z', 'a'..'z']); + SetString(FTokenString, TokenStart, P - TokenStart); + if CompareText(FTokenString, 'NOT')= 0 then { do not localize } + begin + while (P^ <> #0) and (P^ <= ' ') do Inc(P); + TokenStart := P; + Skip(['A'..'Z', 'a'..'z']); + SetString(FTokenString, TokenStart, P - TokenStart); + if CompareText(FTokenString, 'NULL') = 0 then + FToken := etISNOTNULL + else + DatabaseError(SInvalidKeywordUse); + end + else if CompareText (FTokenString, 'NULL') = 0 then { do not localize } + begin + FToken := etISNULL; + end + else + DatabaseError(SInvalidKeywordUse); + end; + end; + '[': + begin + Inc(P); + TokenStart := P; + P := AnsiStrScan(P, ']'); + if P = nil then DatabaseError(SExprNameError); + SetString(FTokenString, TokenStart, P - TokenStart); + FToken := etName; + Inc(P); + end; + '''': + begin + Inc(P); + L := 0; + while True do + begin + if P^ = #0 then DatabaseError(SExprStringError); + if P^ = '''' then + if EndOfLiteral(P) then + Break; + if L < SizeOf(StrBuf) then + begin + StrBuf[L] := P^; + Inc(L); + end; + Inc(P); + end; + SetString(FTokenString, StrBuf, L); + FToken := etLiteral; + FNumericLit := False; + end; + '-', '0'..'9': + begin + if (FPrevToken <> etLiteral) and (FPrevToken <> etName) and + (FPrevToken <> etSymbol)and (FPrevToken <> etRParen) then + begin + TokenStart := P; + Inc(P); + while CharInSet(P^, ['0'..'9', FDecimalSeparator, 'e', 'E', '+', '-']) do + Inc(P); + if ((P-1)^ = ',') and (FDecimalSeparator = ',') and (P^ = ' ') then + Dec(P); + SetString(FTokenString, TokenStart, P - TokenStart); + FToken := etLiteral; + FNumericLit := True; + end + else + begin + FToken := etSUB; + Inc(P); + end; + end; + '(': + begin + Inc(P); + FToken := etLParen; + end; + ')': + begin + Inc(P); + FToken := etRParen; + end; + '<': + begin + Inc(P); + case P^ of + '=': + begin + Inc(P); + FToken := etLE; + end; + '>': + begin + Inc(P); + FToken := etNE; + end; + else + FToken := etLT; + end; + end; + '=': + begin + Inc(P); + FToken := etEQ; + end; + '>': + begin + Inc(P); + if P^ = '=' then + begin + Inc(P); + FToken := etGE; + end else + FToken := etGT; + end; + '+': + begin + Inc(P); + FToken := etADD; + end; + '*': + begin + Inc(P); + FToken := etMUL; + end; + '/': + begin + Inc(P); + FToken := etDIV; + end; + ',': + begin + Inc(P); + FToken := etComma; + end; + #0: + FToken := etEnd; + else + DatabaseErrorFmt(SExprInvalidChar, [P^]); + end; + FSourcePtr := P; +end; + +function TExprParser.ParseExpr: PExprNode; +begin + Result := ParseExpr2; + while TokenSymbolIs('OR') do + begin + NextToken; + Result := FFilter.NewNode(enOperator, coOR, Unassigned, + Result, ParseExpr2); + GetScopeKind(Result, Result^.FLeft, Result^.FRight); + Result^.FDataType := ftBoolean; + end; +end; + +function TExprParser.ParseExpr2: PExprNode; +begin + Result := ParseExpr3; + while TokenSymbolIs('AND') do + begin + NextToken; + Result := FFilter.NewNode(enOperator, coAND, Unassigned, + Result, ParseExpr3); + GetScopeKind(Result, Result^.FLeft, Result^.FRight); + Result^.FDataType := ftBoolean; + end; +end; + +function TExprParser.ParseExpr3: PExprNode; +begin + if TokenSymbolIs('NOT') then + begin + NextToken; + Result := FFilter.NewNode(enOperator, coNOT, Unassigned, + ParseExpr4, nil); + Result^.FDataType := ftBoolean; + end else + Result := ParseExpr4; + GetScopeKind(Result, Result^.FLeft, Result^.FRight); +end; + + +function TExprParser.ParseExpr4: PExprNode; +const + Operators: array[etEQ..etLT] of TCANOperator = ( + coEQ, coNE, coGE, coLE, coGT, coLT); +var + Operator: TCANOperator; + Left, Right: PExprNode; +begin + Result := ParseExpr5; + if (FToken in [etEQ..etLT]) or (FToken = etLIKE) + or (FToken = etISNULL) or (FToken = etISNOTNULL) + or (FToken = etIN) then + begin + case FToken of + etEQ..etLT: + Operator := Operators[FToken]; + etLIKE: + Operator := coLIKE; + etISNULL: + Operator := coISBLANK; + etISNOTNULL: + Operator := coNOTBLANK; + etIN: + Operator := coIN; + else + Operator := coNOTDEFINED; + end; + NextToken; + Left := Result; + if Operator = coIN then + begin + if FToken <> etLParen then + DatabaseErrorFmt(SExprNoLParen, [TokenName]); + NextToken; + Result := FFilter.NewNode(enOperator, coIN, Unassigned, + Left, nil); + Result.FDataType := ftBoolean; + if FToken <> etRParen then + begin + Result.FArgs := TList.Create; + repeat + Right := ParseExpr; + if IsTemporal(Left.FDataType) then + Right.FDataType := Left.FDataType; + Result.FArgs.Add(Right); + if (FToken <> etComma) and (FToken <> etRParen) then + DatabaseErrorFmt(SExprNoRParenOrComma, [TokenName]); + if FToken = etComma then NextToken; + until (FToken = etRParen) or (FToken = etEnd); + if FToken <> etRParen then + DatabaseErrorFmt(SExprNoRParen, [TokenName]); + NextToken; + end else + DatabaseError(SExprEmptyInList); + end else + begin + if (Operator <> coISBLANK) and (Operator <> coNOTBLANK) then + Right := ParseExpr5 + else + Right := nil; + Result := FFilter.NewNode(enOperator, Operator, Unassigned, + Left, Right); + if Right <> nil then + begin + if (Left^.FKind = enField) and (Right^.FKind = enConst) then + begin + Right^.FDataType := Left^.FDataType; + Right^.FDataSize := Left^.FDataSize; + end + else if (Right^.FKind = enField) and (Left^.FKind = enConst) then + begin + Left^.FDataType := Right^.FDataType; + Left^.FDataSize := Right^.FDataSize; + end; + end; + if (Left^.FDataType in BlobFieldTypes) and (Operator = coLIKE) then + begin + if Right^.FKind = enConst then Right^.FDataType := ftString; + end + else if (Operator <> coISBLANK) and (Operator <> coNOTBLANK) + and ((Left^.FDataType in (BlobFieldTypes + [ftBytes])) or + ((Right <> nil) and (Right^.FDataType in (BlobFieldTypes + [ftBytes])))) then + DatabaseError(SExprTypeMis); + Result.FDataType := ftBoolean; + if Right <> nil then + begin + if IsTemporal(Left.FDataType) and (Right.FDataType in StringFieldTypes) then + Right.FDataType := Left.FDataType + else if IsTemporal(Right.FDataType) and (Left.FDataType in StringFieldTypes) then + Left.FDataType := Right.FDataType; + end; + GetScopeKind(Result, Left, Right); + end; + end; +end; + +function TExprParser.ParseExpr5: PExprNode; +const + Operators: array[etADD..etDIV] of TCANOperator = ( + coADD, coSUB, coMUL, coDIV); +var + Operator: TCANOperator; + Left, Right: PExprNode; +begin + Result := ParseExpr6; + while FToken in [etADD, etSUB] do + begin + if not (poExtSyntax in FParserOptions) then + DatabaseError(SExprNoArith); + Operator := Operators[FToken]; + Left := Result; + NextToken; + Right := ParseExpr6; + Result := FFilter.NewNode(enOperator, Operator, Unassigned, Left, Right); + TypeCheckArithOp(Result); + GetScopeKind(Result, Left, Right); + end; +end; + +function TExprParser.ParseExpr6: PExprNode; +const + Operators: array[etADD..etDIV] of TCANOperator = ( + coADD, coSUB, coMUL, coDIV); +var + Operator: TCANOperator; + Left, Right: PExprNode; +begin + Result := ParseExpr7; + while FToken in [etMUL, etDIV] do + begin + if not (poExtSyntax in FParserOptions) then + DatabaseError(SExprNoArith); + Operator := Operators[FToken]; + Left := Result; + NextToken; + Right := ParseExpr7; + Result := FFilter.NewNode(enOperator, Operator, Unassigned, Left, Right); + TypeCheckArithOp(Result); + GetScopeKind(Result, Left, Right); + end; +end; + + +function TExprParser.ParseExpr7: PExprNode; +var + FuncName: string; +begin + case FToken of + etSymbol: + if (poExtSyntax in FParserOptions) + and NextTokenIsLParen and TokenSymbolIsFunc(FTokenString) then + begin + Funcname := FTokenString; + NextToken; + if FToken <> etLParen then + DatabaseErrorFmt(SExprNoLParen, [TokenName]); + NextToken; + if (CompareText(FuncName,'count') = 0) and (FToken = etMUL) then + begin + FuncName := 'COUNT(*)'; + NextToken; + end; + Result := FFilter.NewNode(enFunc, coNOTDEFINED, FuncName, + nil, nil); + if FToken <> etRParen then + begin + Result.FArgs := TList.Create; + repeat + Result.FArgs.Add(ParseExpr); + if (FToken <> etComma) and (FToken <> etRParen) then + DatabaseErrorFmt(SExprNoRParenOrComma, [TokenName]); + if FToken = etComma then NextToken; + until (FToken = etRParen) or (FToken = etEnd); + end else + Result.FArgs := nil; + + GetFuncResultInfo(Result); + end + else if TokenSymbolIs('NULL') then + begin + Result := FFilter.NewNode(enConst, coNOTDEFINED, {$IFDEF DELPHI_6}Variants.{$ENDIF}Null, nil, nil); + Result.FScopeKind := skConst; + end + else if TokenSymbolIs(FStrTrue) then + begin + Result := FFilter.NewNode(enConst, coNOTDEFINED, 1, nil, nil); + Result.FScopeKind := skConst; + end + else if TokenSymbolIs(FStrFalse) then + begin + Result := FFilter.NewNode(enConst, coNOTDEFINED, 0, nil, nil); + Result.FScopeKind := skConst; + end + else + begin + Result := FFilter.NewNode(enField, coNOTDEFINED, FTokenString, nil, nil); + Result.FScopeKind := skField; + end; + etName: + begin + Result := FFilter.NewNode(enField, coNOTDEFINED, FTokenString, nil, nil); + Result.FScopeKind := skField; + end; + etLiteral: + begin + Result := FFilter.NewNode(enConst, coNOTDEFINED, FTokenString, nil, nil); + if FNumericLit then Result^.FDataType := ftFloat else + Result^.FDataType := ftString; + Result.FScopeKind := skConst; + end; + etLParen: + begin + NextToken; + Result := ParseExpr; + if FToken <> etRParen then DatabaseErrorFmt(SExprNoRParen, [TokenName]); + end; + else + DatabaseErrorFmt(SExprExpected, [TokenName]); + Result := nil; + end; + NextToken; +end; + +procedure TExprParser.GetScopeKind(Root, Left, Right : PExprNode); +begin + if (Left = nil) and (Right = nil) then Exit; + if Right = nil then + begin + Root.FScopeKind := Left.FScopeKind; + Exit; + end; + if ((Left^.FScopeKind = skField) and (Right^.FScopeKind = skAgg)) + or ((Left^.FScopeKind = skAgg) and (Right^.FScopeKind = skField)) then + DatabaseError(SExprBadScope); + if (Left^.FScopeKind = skConst) and (Right^.FScopeKind = skConst) then + Root^.FScopeKind := skConst + else if (Left^.FScopeKind = skAgg) or (Right^.FScopeKind = skAgg) then + Root^.FScopeKind := skAgg + else if (Left^.FScopeKind = skField) or (Right^.FScopeKind = skField) then + Root^.FScopeKind := skField; +end; + +procedure TExprParser.GetFuncResultInfo(Node : PExprNode); +begin + Node^.FDataType := ftString; + if (CompareText(Node^.FData, 'COUNT(*)') <> 0 ) + and (CompareText(Node^.FData,'GETDATE') <> 0 ) + and ( (Node^.FArgs = nil ) or ( Node^.FArgs.Count = 0) ) then + DatabaseError(SExprTypeMis); + + if (Node^.FArgs <> nil) and (Node^.FArgs.Count > 0) then + Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; + if (CompareText(Node^.FData , 'SUM') = 0) or + (CompareText(Node^.FData , 'AVG') = 0) then + begin + Node^.FDataType := ftFloat; + Node^.FScopeKind := skAgg; + end + else if (CompareText(Node^.FData , 'MIN') = 0) or + (CompareText(Node^.FData , 'MAX') = 0) then + begin + Node^.FDataType := PExprNode(Node^.FArgs.Items[0])^.FDataType; + Node^.FScopeKind := skAgg; + end + else if (CompareText(Node^.FData , 'COUNT') = 0) or + (CompareText(Node^.FData , 'COUNT(*)') = 0) then + begin + Node^.FDataType := ftInteger; + Node^.FScopeKind := skAgg; + end + else if (CompareText(Node^.FData , 'YEAR') = 0) or + (CompareText(Node^.FData , 'MONTH') = 0) or + (CompareText(Node^.FData , 'DAY') = 0) or + (CompareText(Node^.FData , 'HOUR') = 0) or + (CompareText(Node^.FData , 'MINUTE') = 0) or + (CompareText(Node^.FData , 'SECOND') = 0 ) then + begin + Node^.FDataType := ftInteger; + Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; + end + else if CompareText(Node^.FData , 'GETDATE') = 0 then + begin + Node^.FDataType := ftDateTime; + Node^.FScopeKind := skConst; + end + else if CompareText(Node^.FData , 'DATE') = 0 then + begin + Node^.FDataType := ftDate; + Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; + end + else if CompareText(Node^.FData , 'TIME') = 0 then + begin + Node^.FDataType := ftTime; + Node^.FScopeKind := PExprNode(Node^.FArgs.Items[0])^.FScopeKind; + end; +end; + +function TExprParser.TokenName: string; +begin + if FSourcePtr = FTokenPtr then Result := SExprNothing else + begin + SetString(Result, FTokenPtr, FSourcePtr - FTokenPtr); + Result := '''' + Result + ''''; + end; +end; + +function TExprParser.TokenSymbolIs(const S: string): Boolean; +begin + Result := (FToken = etSymbol) and (CompareText(FTokenString, S) = 0); +end; + + +function TExprParser.TokenSymbolIsFunc(const S: string) : Boolean; +begin + Result := (CompareText(S, 'UPPER') = 0) or + (CompareText(S, 'LOWER') = 0) or + (CompareText(S, 'SUBSTRING') = 0) or + (CompareText(S, 'TRIM') = 0) or + (CompareText(S, 'TRIMLEFT') = 0) or + (CompareText(S, 'TRIMRIGHT') = 0) or + (CompareText(S, 'YEAR') = 0) or + (CompareText(S, 'MONTH') = 0) or + (CompareText(S, 'DAY') = 0) or + (CompareText(S, 'HOUR') = 0) or + (CompareText(S, 'MINUTE') = 0) or + (CompareText(S, 'SECOND') = 0) or + (CompareText(S, 'GETDATE') = 0) or + (CompareText(S, 'DATE') = 0) or + (CompareText(S, 'TIME') = 0) or + (CompareText(S, 'SUM') = 0) or + (CompareText(S, 'MIN') = 0) or + (CompareText(S, 'MAX') = 0) or + (CompareText(S, 'AVG') = 0) or + (CompareText(S, 'COUNT') = 0); + +end; + +procedure TExprParser.TypeCheckArithOp(Node: PExprNode); +begin + with Node^ do + begin + if IsNumeric(FLeft.FDataType) and IsNumeric(FRight.FDataType) then + FDataType := ftFloat + else if (FLeft.FDataType in StringFieldTypes) and + (FRight.FDataType in StringFieldTypes) and (FOperator = coADD) then + FDataType := ftString + else if IsTemporal(FLeft.FDataType) and IsNumeric(FRight.FDataType) and + (FOperator = coADD) then + FDataType := ftDateTime + else if IsTemporal(FLeft.FDataType) and IsNumeric(FRight.FDataType) and + (FOperator = coSUB) then + FDataType := FLeft.FDataType + else if IsTemporal(FLeft.FDataType) and IsTemporal(FRight.FDataType) and + (FOperator = coSUB) then + FDataType := ftFloat + else if (FLeft.FDataType in StringFieldTypes) and IsTemporal(FRight.FDataType) and + (FOperator = coSUB) then + begin + FLeft.FDataType := FRight.FDataType; + FDataType := ftFloat; + end + else if ( FLeft.FDataType in StringFieldTypes) and IsNumeric(FRight.FDataType )and + (FLeft.FKind = enConst) then + FLeft.FDataType := ftDateTime + else + DatabaseError(SExprTypeMis); + end; +end; + + +end. + + + diff --git a/PSQLConnFrm.dfm b/Source/PSQLConnFrm.dfm similarity index 94% rename from PSQLConnFrm.dfm rename to Source/PSQLConnFrm.dfm index 3f257b4..e0ab690 100644 --- a/PSQLConnFrm.dfm +++ b/Source/PSQLConnFrm.dfm @@ -1,147 +1,147 @@ -object PSQLConnForm: TPSQLConnForm - Left = 381 - Top = 210 - BorderStyle = bsDialog - Caption = 'TPSQLDatabase Editor...' - ClientHeight = 240 - ClientWidth = 340 - Color = clBtnFace - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - OldCreateOrder = False - Position = poScreenCenter - PixelsPerInch = 96 - TextHeight = 13 - object Bevel1: TBevel - Left = 0 - Top = 25 - Width = 340 - Height = 176 - Align = alTop - Shape = bsFrame - end - object laUser: TLabel - Left = 8 - Top = 65 - Width = 39 - Height = 13 - Caption = '&User ID:' - FocusControl = DBUserID - end - object laPass: TLabel - Left = 8 - Top = 93 - Width = 49 - Height = 13 - Caption = '&Password:' - FocusControl = DBPasswd - end - object laDbName: TLabel - Left = 8 - Top = 36 - Width = 80 - Height = 13 - Caption = 'Database &Name:' - FocusControl = DBName - end - object laHost: TLabel - Left = 8 - Top = 122 - Width = 68 - Height = 13 - Caption = '&Host Name/IP' - FocusControl = DBHost - end - object laPort: TLabel - Left = 8 - Top = 150 - Width = 56 - Height = 13 - Caption = 'Server &Port:' - FocusControl = DBPort - end - object DBUserID: TEdit - Left = 96 - Top = 63 - Width = 233 - Height = 21 - TabOrder = 1 - end - object DBPasswd: TEdit - Left = 96 - Top = 91 - Width = 233 - Height = 21 - PasswordChar = '*' - TabOrder = 2 - end - object DBName: TEdit - Left = 96 - Top = 34 - Width = 233 - Height = 21 - TabOrder = 0 - end - object DBHost: TEdit - Left = 96 - Top = 120 - Width = 233 - Height = 21 - TabOrder = 3 - end - object DBPort: TEdit - Left = 96 - Top = 148 - Width = 233 - Height = 21 - TabOrder = 4 - end - object DBLogin: TCheckBox - Left = 8 - Top = 180 - Width = 97 - Height = 17 - Caption = '&Login Prompt:' - TabOrder = 5 - end - object OkBtn: TButton - Left = 176 - Top = 206 - Width = 75 - Height = 25 - Caption = '&OK' - Default = True - ModalResult = 1 - TabOrder = 6 - end - object CancelBtn: TButton - Left = 260 - Top = 206 - Width = 75 - Height = 25 - Caption = '&Cancel' - ModalResult = 2 - TabOrder = 7 - end - object Panel1: TPanel - Left = 0 - Top = 0 - Width = 340 - Height = 25 - Align = alTop - Alignment = taLeftJustify - BevelOuter = bvNone - Caption = ' PostgreSQL Connection Options' - Color = clGray - Font.Charset = DEFAULT_CHARSET - Font.Color = clWhite - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [fsBold] - ParentFont = False - TabOrder = 8 - end -end +object PSQLConnForm: TPSQLConnForm + Left = 381 + Top = 210 + BorderStyle = bsDialog + Caption = 'TPSQLDatabase Editor...' + ClientHeight = 240 + ClientWidth = 340 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + PixelsPerInch = 96 + TextHeight = 13 + object Bevel1: TBevel + Left = 0 + Top = 25 + Width = 340 + Height = 176 + Align = alTop + Shape = bsFrame + end + object laUser: TLabel + Left = 8 + Top = 65 + Width = 39 + Height = 13 + Caption = '&User ID:' + FocusControl = DBUserID + end + object laPass: TLabel + Left = 8 + Top = 93 + Width = 49 + Height = 13 + Caption = '&Password:' + FocusControl = DBPasswd + end + object laDbName: TLabel + Left = 8 + Top = 36 + Width = 80 + Height = 13 + Caption = 'Database &Name:' + FocusControl = DBName + end + object laHost: TLabel + Left = 8 + Top = 122 + Width = 68 + Height = 13 + Caption = '&Host Name/IP' + FocusControl = DBHost + end + object laPort: TLabel + Left = 8 + Top = 150 + Width = 56 + Height = 13 + Caption = 'Server &Port:' + FocusControl = DBPort + end + object DBUserID: TEdit + Left = 96 + Top = 63 + Width = 233 + Height = 21 + TabOrder = 1 + end + object DBPasswd: TEdit + Left = 96 + Top = 91 + Width = 233 + Height = 21 + PasswordChar = '*' + TabOrder = 2 + end + object DBName: TEdit + Left = 96 + Top = 34 + Width = 233 + Height = 21 + TabOrder = 0 + end + object DBHost: TEdit + Left = 96 + Top = 120 + Width = 233 + Height = 21 + TabOrder = 3 + end + object DBPort: TEdit + Left = 96 + Top = 148 + Width = 233 + Height = 21 + TabOrder = 4 + end + object DBLogin: TCheckBox + Left = 8 + Top = 180 + Width = 97 + Height = 17 + Caption = '&Login Prompt:' + TabOrder = 5 + end + object OkBtn: TButton + Left = 176 + Top = 206 + Width = 75 + Height = 25 + Caption = '&OK' + Default = True + ModalResult = 1 + TabOrder = 6 + end + object CancelBtn: TButton + Left = 260 + Top = 206 + Width = 75 + Height = 25 + Caption = '&Cancel' + ModalResult = 2 + TabOrder = 7 + end + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 340 + Height = 25 + Align = alTop + Alignment = taLeftJustify + BevelOuter = bvNone + Caption = ' PostgreSQL Connection Options' + Color = clGray + Font.Charset = DEFAULT_CHARSET + Font.Color = clWhite + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [fsBold] + ParentFont = False + TabOrder = 8 + end +end diff --git a/PSQLConnFrm.pas b/Source/PSQLConnFrm.pas similarity index 95% rename from PSQLConnFrm.pas rename to Source/PSQLConnFrm.pas index 1fb90ee..b5556aa 100644 --- a/PSQLConnFrm.pas +++ b/Source/PSQLConnFrm.pas @@ -1,91 +1,91 @@ -unit PSQLConnFrm; - -interface - -{SVN revision: $Id$} - -uses - SysUtils, Classes, Graphics, Controls, Forms, Dialogs, - StdCtrls, ExtCtrls, PSQLDbTables, PSQLTypes; - -type - TPSQLConnForm = class(TForm) - Bevel1: TBevel; - laUser: TLabel; - laPass: TLabel; - DBUserID: TEdit; - DBPasswd: TEdit; - laDbName: TLabel; - DBName: TEdit; - laHost: TLabel; - DBHost: TEdit; - laPort: TLabel; - DBPort: TEdit; - DBLogin: TCheckBox; - OkBtn: TButton; - CancelBtn: TButton; - Panel1: TPanel; - private - { Private declarations } - Database: TPSQLDatabase; - function Edit: Boolean; - public - { Public declarations } - procedure GetDatabaseProperty(Db: TPSQLDatabase); - procedure SetDatabaseProperty(Db: TPSQLDatabase); - end; - -function EditDatabase(ADatabase: TPSQLDatabase): Boolean; - -var - PSQLConnForm: TPSQLConnForm; - -implementation - -{$R *.DFM} - -function EditDatabase(ADatabase: TPSQLDatabase): Boolean; -begin - with TPSQLConnForm.Create(Application) do - try - Database := ADatabase; - Result := Edit; - finally - Free; - end; -end; - -function TPSQLConnForm.Edit: Boolean; -begin - GetDatabaseProperty(Database); - Result := False; - if ShowModal = mrOk then - begin - SetDatabaseProperty(Database); - Result := True; - end; -end; - -procedure TPSQLConnForm.GetDatabaseProperty(Db: TPSQLDatabase); -begin - DBName.Text := DB.DatabaseName; - DBUserId.Text := db.UserName; - DBPasswd.Text := db.UserPassword; - DBHost.Text := Db.Host; - DBPort.Text := IntToStr(Db.Port); - DBLogin.Checked := db.LoginPrompt; -end; - -procedure TPSQLConnForm.SetDatabaseProperty(Db: TPSQLDatabase); -begin - DB.DatabaseName := DBName.Text; - db.UserName := DBUserId.Text; - db.UserPassword := DBPasswd.Text; - Db.Host := DBHost.Text; - Db.Port := StrToIntDef(DBPort.Text, PSQLTypes.PSQL_PORT); - db.LoginPrompt := DBLogin.Checked; -end; - -end. - - +unit PSQLConnFrm; + +interface + +{SVN revision: $Id$} + +uses + SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, ExtCtrls, PSQLDbTables, PSQLTypes; + +type + TPSQLConnForm = class(TForm) + Bevel1: TBevel; + laUser: TLabel; + laPass: TLabel; + DBUserID: TEdit; + DBPasswd: TEdit; + laDbName: TLabel; + DBName: TEdit; + laHost: TLabel; + DBHost: TEdit; + laPort: TLabel; + DBPort: TEdit; + DBLogin: TCheckBox; + OkBtn: TButton; + CancelBtn: TButton; + Panel1: TPanel; + private + { Private declarations } + Database: TPSQLDatabase; + function Edit: Boolean; + public + { Public declarations } + procedure GetDatabaseProperty(Db: TPSQLDatabase); + procedure SetDatabaseProperty(Db: TPSQLDatabase); + end; + +function EditDatabase(ADatabase: TPSQLDatabase): Boolean; + +var + PSQLConnForm: TPSQLConnForm; + +implementation + +{$R *.DFM} + +function EditDatabase(ADatabase: TPSQLDatabase): Boolean; +begin + with TPSQLConnForm.Create(Application) do + try + Database := ADatabase; + Result := Edit; + finally + Free; + end; +end; + +function TPSQLConnForm.Edit: Boolean; +begin + GetDatabaseProperty(Database); + Result := False; + if ShowModal = mrOk then + begin + SetDatabaseProperty(Database); + Result := True; + end; +end; + +procedure TPSQLConnForm.GetDatabaseProperty(Db: TPSQLDatabase); +begin + DBName.Text := DB.DatabaseName; + DBUserId.Text := db.UserName; + DBPasswd.Text := db.UserPassword; + DBHost.Text := Db.Host; + DBPort.Text := IntToStr(Db.Port); + DBLogin.Checked := db.LoginPrompt; +end; + +procedure TPSQLConnForm.SetDatabaseProperty(Db: TPSQLDatabase); +begin + DB.DatabaseName := DBName.Text; + db.UserName := DBUserId.Text; + db.UserPassword := DBPasswd.Text; + Db.Host := DBHost.Text; + Db.Port := StrToIntDef(DBPort.Text, PSQLTypes.PSQL_PORT); + db.LoginPrompt := DBLogin.Checked; +end; + +end. + + diff --git a/PSQLCopy.pas b/Source/PSQLCopy.pas similarity index 96% rename from PSQLCopy.pas rename to Source/PSQLCopy.pas index 59c8c47..dffe0ef 100644 --- a/PSQLCopy.pas +++ b/Source/PSQLCopy.pas @@ -1,648 +1,648 @@ -{$I pSQLDAC.inc} - -unit PSQLCopy; - -{SVN revision: $Id$} - -interface - -uses {$IFDEF FPC}LCLIntf,{$ENDIF} SysUtils, Classes, PSQLTypes, - PSQLAccess, PSQLDbTables; - -type - - TCopyDirection = (cdIn, cdOut); //in = put, out = get - - TCopyMode = (cmFile, cmSTDInOut, cmProgram); //copy to file, to active connection or to process launched by server - - TCopyFormat = (cfText, cfCSV, cfBinary); - - EPSQLCopyException = class(Exception); - - TCopyOption = (coUseOIDs, coBinary, coCSV, coHeader, coNULL, coDelimiter, coQuote, coEscape, coFreeze); - TCopyOptions = set of TCopyOption; - - TAbstractCopyObject = class(TComponent) - private - FColumns: TStrings; - FDelimiter: char; - FNullValue: string; - FFileName: string; - FCommandLine: string; - FTableName: TFileName; - FDatabase: TPSQLDatabase; - FOptions: TCopyOptions; - FForcedColumns: TStrings; - FSQL: TStrings; - FEscape: char; - FQuote: char; - FRowsAffected: Integer; - FEncoding: string; - procedure SetColumns(const Value: TStrings); - procedure SetDatabase(const Value: TPSQLDatabase); - procedure SetOptions(const Value: TCopyOptions); - procedure SetForcedColumns(const Value: TStrings); - procedure SetSQL(const Value: TStrings); - procedure SetEscape(const Value: char); - procedure SetQuote(const Value: char); - procedure SetDelimiter(const Value: char); - procedure SetNullValue(const Value: string); - function GetDataFormat: TCopyFormat; - procedure SetDataFormat(const Value: TCopyFormat); - procedure SetEncoding(const Value: string); - protected - function GetSQLStatement: string; virtual; abstract; - procedure DoServerSideCopyGet; virtual; abstract; - procedure DoClientSideCopyGet(Stream: TStream); virtual; abstract; - procedure DoServerSideCopyPut; virtual; abstract; - procedure DoClientSideCopyPut(Stream: TStream); virtual; abstract; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - property Delimiter: char read FDelimiter write SetDelimiter; - property Quote: char read FQuote write SetQuote; - property Escape: char read FEscape write SetEscape; - property NullValue: string read FNullValue write SetNullValue; - property Tablename: TFileName read FTablename write FTablename; - property Columns: TStrings read FColumns write SetColumns; - property ForcedColumns: TStrings read FForcedColumns write SetForcedColumns; - property Database: TPSQLDatabase read FDatabase write SetDatabase; - property Options: TCopyOptions read FOptions write SetOptions default []; - property SQL: TStrings read FSQL write SetSQL; - property RowsAffected: Integer read FRowsAffected; - property DataFormat: TCopyFormat read GetDataFormat write SetDataFormat; - property Encoding: string read FEncoding write SetEncoding; - end; - - TCustomPSQLCopy = class(TAbstractCopyObject) - protected - FCopyDirection: TCopyDirection; - FCopyMode: TCopyMode; - FAfterCopyGet: TNotifyEvent; - FBeforeCopyGet: TNotifyEvent; - FAfterCopyPut: TNotifyEvent; - FBeforeCopyPut: TNotifyEvent; - function GetCommaSeparatedText(const AStrings: TStrings):string; - function GetSQLStatement: string; override; - procedure DoServerSideCopyGet; override; - procedure DoClientSideCopyGet(Stream: TStream); override; - procedure DoServerSideCopyPut; override; - procedure DoClientSideCopyPut(Stream: TStream); override; - public - procedure LoadFromStream(Stream: TStream); - procedure SaveToStream(Stream: TStream); - - procedure LoadFromStrings(Strings: TStrings); - procedure SaveToStrings(Strings: TStrings); overload; -{$IFDEF DELPHI_15} - procedure SaveToStrings(Strings: TStrings; Encoding: TEncoding); overload; -{$ENDIF} - - procedure LoadFromClientSideFile(const FileName: string); - procedure SaveToClientSideFile(const FileName: string); - - procedure LoadFromServerSideFile(const FileName: string); - procedure SaveToServerSideFile(const FileName: string); - - procedure LoadFromProgram(const CommandLine: string); - procedure SaveToProgram(const CommandLine: string); - - property BeforeCopyGet: TNotifyEvent read FBeforeCopyGet write FBeforeCopyGet; - property AfterCopyGet: TNotifyEvent read FAfterCopyGet write FAfterCopyGet; - property AfterCopyPut: TNotifyEvent read FAfterCopyPut write FAfterCopyPut; - property BeforeCopyPut: TNotifyEvent read FBeforeCopyPut write FBeforeCopyPut; - end; - - - TPSQLCopy = class(TCustomPSQLCopy) - published - property Delimiter; - property Quote; - property Escape; - property NullValue; - property Tablename; - property Columns; - property ForcedColumns; - property Database; - property Options; - property SQL; - property DataFormat; - property Encoding; - property BeforeCopyGet; - property AfterCopyGet; - property BeforeCopyPut; - property AfterCopyPut; - end; - -implementation - -uses DB{$IFNDEF DELPHI_5}, StrUtils{$ENDIF}; - -{ TAbstractCopyObject } - -constructor TAbstractCopyObject.Create(AOwner: TComponent); -var I: integer; -begin - inherited; - FColumns := TStringList.Create; - FForcedColumns := TStringList.Create; - FSQL := TStringList.Create; - FDelimiter := #0; - FNullValue := ''; - FTableName := ''; - if (csDesigning in ComponentState) and Assigned(AOwner) and (DBList.Count > 0) then - begin - for I := DBList.Count - 1 downto 0 do - if TCustomConnection(DBList[I]).Owner = AOwner then - begin - Database := TPSQLDatabase(DBList[I]); - Break; - end; - if not Assigned(Database) then - Database := TPSQLDatabase(DBList[DBList.Count - 1]); - end; -end; - -destructor TAbstractCopyObject.Destroy; -begin - FColumns.Free; - FForcedColumns.Free; - FSQL.Free; - inherited Destroy; -end; - -function TAbstractCopyObject.GetDataFormat: TCopyFormat; -begin - if coCSV in FOptions then - Result := cfCSV - else - if coBinary in FOptions then - Result := cfBinary - else - Result := cfText; -end; - -procedure TAbstractCopyObject.SetColumns(const Value: TStrings); -begin - if Assigned(Value) then FColumns.Assign(Value); -end; - -procedure TAbstractCopyObject.SetDatabase(const Value: TPSQLDatabase); -begin - if (Value <> FDatabase) then FDatabase := Value; -end; - -procedure TCustomPSQLCopy.DoClientSideCopyGet(Stream: TStream); -var Result: PPGresult; - LineRes: integer; - Buffer: PAnsiDACChar; - S: DACAString; - AConnect: TNativeConnect; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - if Assigned(FBeforeCopyGet) then - FBeforeCopyGet(Self); - FRowsAffected := -1; - if not FDatabase.Connected then DatabaseError(SDatabaseClosed); - AConnect := TNativeConnect(FDatabase.Handle); - Result := _PQExecute(AConnect, GetSQLStatement); - try - Stream.Position := 0; - Stream.Size := 0; - try - if PQresultStatus(Result) = PGRES_COPY_OUT then - begin - Repeat - LineRes := PQgetCopyData(AConnect.Handle, @Buffer); - if (LineRes > 0) and Assigned(Buffer) then - begin - S := {$IFDEF NEXTGEN}string{$ENDIF}(Copy(Buffer,1,LineRes)); - Stream.Write(Pointer( - {$IFNDEF NEXTGEN}S{$ELSE}M.AsAnsi(S).ToPointer{$ENDIF} - )^,length(S)); - end; - if Buffer <> nil then - PQfreemem (Buffer); - Until LineRes < 0; - if PQresultStatus(Result) <> PGRES_COMMAND_OK then - AConnect.CheckResult; - if Assigned(FAfterCopyGet) then - FAfterCopyGet(Self); - FRowsAffected := StrToIntDef(String(PQcmdTuples(Result)), -1); - end - else - AConnect.CheckResult; - except - on E: EPSQLException do - raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); - else - raise; - end; - finally - PQClear(Result); - end; -end; - -procedure TCustomPSQLCopy.DoClientSideCopyPut(Stream: TStream); -var Result, Result2: PPGresult; - Count: cardinal; - Buffer: array[Word] of AnsiDACByteChar; - AConnect: TNativeConnect; -begin - if Assigned(FBeforeCopyPut) then - FBeforeCopyPut(Self); - FRowsAffected := -1; - if not FDatabase.Connected then DatabaseError(SDatabaseClosed); - AConnect := TNativeConnect(FDatabase.Handle); - Result := _PQExecute(AConnect, GetSQLStatement); - try - try - Stream.Position := 0; - if PQresultStatus(Result) = PGRES_COPY_IN then - begin - Count := Stream.Read(Buffer[0], Length(Buffer)); - while Count > 0 do - if PQputCopyData(AConnect.Handle, {$IFNDEF NEXTGEN} - Buffer - {$ELSE} - @Buffer - {$ENDIF}, - Count) <= 0 then - AConnect.CheckResult(Result) - else - Count := Stream.Read(Buffer,Length(Buffer)); - if PQputCopyEnd(AConnect.Handle) <= 0 then - AConnect.CheckResult(Result) - else - begin - Result2 := PQgetResult(AConnect.Handle); - try - AConnect.CheckResult(Result2); - FRowsAffected := StrToIntDef(String(PQcmdTuples(Result2)), -1); - finally - PQClear(Result2); - end; - end; - if Assigned(FAfterCopyPut) then - FAfterCopyPut(Self); - end - else - AConnect.CheckResult(Result); - finally - PQClear(Result); - end; - except - on E: EPSQLException do - raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); - else - raise; - end; -end; - -procedure TCustomPSQLCopy.DoServerSideCopyGet; -var - Result: PPGResult; - AConnect: TNativeConnect; -begin - if Assigned(FBeforeCopyGet) then - FBeforeCopyGet(Self); - FRowsAffected := -1; - if not FDatabase.Connected then DatabaseError(SDatabaseClosed); - AConnect := TNativeConnect(FDatabase.Handle); - Result := _PQExecute(AConnect, GetSQLStatement); - try - try - AConnect.CheckResult(Result); - FRowsAffected := StrToIntDef(String(PQcmdTuples(Result)), -1); - finally - PQClear(Result); - end; - except - on E: EPSQLException do - raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); - else - raise; - end; - if Assigned(FAfterCopyGet) then - FAfterCopyGet(Self); -end; - -procedure TCustomPSQLCopy.DoServerSideCopyPut; - var - Result: PPGResult; - AConnect: TNativeConnect; -begin - if Assigned(FBeforeCopyPut) then - FBeforeCopyPut(Self); - FRowsAffected := -1; - if not FDatabase.Connected then DatabaseError(SDatabaseClosed); - AConnect := TNativeConnect(FDatabase.Handle); - Result := _PQExecute(AConnect, GetSQLStatement); - try - try - AConnect.CheckResult; - FRowsAffected := StrToIntDef(String(PQcmdTuples(Result)), -1); - finally - PQClear(Result); - end; - except - on E: EPSQLException do - raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); - else - raise; - end; - if Assigned(FAfterCopyPut) then - FAfterCopyPut(Self); -end; - -function TCustomPSQLCopy.GetCommaSeparatedText( - const AStrings: TStrings): string; -var i: integer; -begin - if AStrings.Count > 0 then - Result := AStrings[0] - else - begin - Result := ''; - Exit; - end; - for i:=1 to AStrings.Count-1 do - Result := Result + ', ' + AStrings[i]; -end; - -function TCustomPSQLCopy.GetSQLStatement: string; - - function GetOldWithStmt: string; - const - cForced: array[TCopyDirection] of string = (' FORCE NOT NULL %s ',' FORCE QUOTE %s '); - begin - if coUseOIDs in FOptions then Result := Result + 'OIDS '; - if coBinary in FOptions then - Result := Result + 'BINARY ' - else - begin - if (coDelimiter in FOptions) and (FDelimiter > #0) then - Result := Result + Format(' DELIMITER %s ',[QuotedStr(FDelimiter)]); - if (coNull in FOptions) and (FNullValue > '') then - Result := Result + Format(' NULL %s ',[QuotedStr(FNullValue)]); - if (coCSV in FOptions) then - begin - Result := Result + ' CSV '; - if (coHeader in FOptions) then - Result := Result + ' HEADER '; - if (coQuote in FOptions) and (FQuote > #0) then - Result := Result + Format(' QUOTE %s ',[FQuote]); - if (coEscape in FOptions) and (FEscape > #0) then - Result := Result + Format(' ESCAPE %s ',[FEscape]); - if FForcedColumns.Count > 0 then - Result := Result + Format(cForced[FCopyDirection],[GetCommaSeparatedText(FForcedColumns)]); - end; - end; - if Result > '' then - Result := ' WITH ' + Result; - end; - - function GetNewWithStmt: string; - const - cFormat: array[TCopyFormat] of string = ('''text''', '''csv''', '''binary'''); - cForced: array[TCopyDirection] of string = (', FORCE_NOT_NULL %s', ', FORCE_QUOTE %s'); - begin - Result := 'FORMAT ' + cFormat[GetDataFormat]; - if coUseOIDs in FOptions then Result := Result + ', OIDS true'; - if coFreeze in FOptions then Result := Result + ', FREEZE true'; - - if not (coBinary in FOptions) then - begin - if (coDelimiter in FOptions) and (FDelimiter > #0) then - Result := Result + Format(', DELIMITER %s ',[QuotedStr(FDelimiter)]); - if (coNull in FOptions) and (FNullValue > '') then - Result := Result + Format(', NULL %s ',[QuotedStr(FNullValue)]); - if (coCSV in FOptions) then - begin - if (coHeader in FOptions) then - Result := Result + ', HEADER true'; - if (coQuote in FOptions) and (FQuote > #0) then - Result := Result + Format(', QUOTE %s ',[QuotedStr(FQuote)]); - if (coEscape in FOptions) and (FEscape > #0) then - Result := Result + Format(', ESCAPE %s ',[QuotedStr(FEscape)]); - if FForcedColumns.Count > 0 then - Result := Result + Format(cForced[FCopyDirection],[GetCommaSeparatedText(FForcedColumns)]); - end; - end; - if FEncoding > '' then - Result := Result + ', ENCODING ' + QuotedStr(FEncoding); - Result := ' WITH (' + Result + ')'; - end; - -begin - if (FCopyDirection = cdOUT) and (FSQL.Count > 0) then - Result := Format('COPY (%s) ',[Trim(FSQL.Text)]) - else - begin - Result := Format('COPY %s ',[FTablename]); - if FColumns.Count > 0 then - Result := Result + '(' + GetCommaSeparatedText(FColumns) + ')'; - end; - Result := Result + ifthen(FCopyDirection = cdOut, ' TO ', ' FROM '); - case FCopyMode of - cmFile: - begin - if FFileName = '' then - if FCopyDirection = cdIn then - raise EPSQLCopyException.Create('FileName is required for putting data.') - else - FFilename := DateTimeToStr(Date) + '.pge'; - Result := Result + QuotedStr(FFileName); - end; - cmSTDInOut: - Result := Result + ifthen(FCopyDirection = cdOUT, 'STDOUT', 'STDIN'); - cmProgram: - Result := Result + 'PROGRAM ' + QuotedStr(FCommandLine); - end; - - if Assigned(FDatabase) then - Result := Result + ifthen(FDatabase.ServerVersionAsInt < 090000, GetOldWithStmt(), GetNewWithStmt()); -end; - - -procedure TCustomPSQLCopy.LoadFromClientSideFile(const FileName: string); -var FS: TFileStream; -begin - FS := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite); - try - LoadFromStream(FS); - finally - FS.Free; - end; -end; - -procedure TCustomPSQLCopy.LoadFromProgram(const CommandLine: string); -begin - FCopyDirection := cdIN; - FCopyMode := cmProgram; - FCommandLine := CommandLine; - DoServerSideCopyPut(); -end; - -procedure TCustomPSQLCopy.LoadFromServerSideFile(const FileName: string); -begin - FCopyDirection := cdIN; - FCopyMode := cmSTDINOUT; - FFileName := FileName; - DoServerSideCopyPut(); -end; - -procedure TCustomPSQLCopy.LoadFromStream(Stream: TStream); -begin - FCopyDirection := cdIN; - FCopyMode := cmSTDINOUT; - if not Assigned(Stream) then Exit; - DoClientSideCopyPut(Stream); -end; - -procedure TCustomPSQLCopy.LoadFromStrings(Strings: TStrings); -var MS: TMemoryStream; -begin - MS := TMemoryStream.Create; - try - Strings.SaveToStream(MS); - LoadFromStream(MS); - finally - MS.Free; - end; -end; - -procedure TCustomPSQLCopy.SaveToClientSideFile(const FileName: string); -var FS: TFileStream; -begin - FS := TFileStream.Create(FileName, fmCreate or fmShareExclusive); - try - SaveToStream(FS); - finally - FS.Free; - end; -end; - -procedure TCustomPSQLCopy.SaveToProgram(const CommandLine: string); -begin - FCopyMode := cmProgram; - FCopyDirection := cdOUT; - FCommandLine := CommandLine; - DoServerSideCopyGet(); -end; - -procedure TCustomPSQLCopy.SaveToServerSideFile(const FileName: string); -begin - FCopyMode := cmFile; - FCopyDirection := cdOut; - FFileName := FileName; - DoServerSideCopyGet(); -end; - -procedure TCustomPSQLCopy.SaveToStream(Stream: TStream); -begin - FCopyDirection := cdOUT; - FCopyMode := cmSTDINOUT; - if not Assigned(Stream) then - raise EPSQLCopyException.Create('Stream not assigned'); - DoClientSideCopyGet(Stream); -end; - -{$IFDEF DELPHI_15} -procedure TCustomPSQLCopy.SaveToStrings(Strings: TStrings; Encoding: TEncoding); -var MS: TMemoryStream; -begin - if Assigned(Strings) then - begin - MS := TMemoryStream.Create; - try - SaveToStream(MS); - MS.Position := 0; - Strings.LoadFromStream(MS, Encoding); - finally - MS.Free; - end; - end; -end; -{$ENDIF} - -procedure TCustomPSQLCopy.SaveToStrings(Strings: TStrings); -var MS: TMemoryStream; -begin - if Assigned(Strings) then - begin - MS := TMemoryStream.Create; - try - SaveToStream(MS); - MS.Position := 0; - Strings.LoadFromStream(MS); - finally - MS.Free; - end; - end; -end; - - -procedure TAbstractCopyObject.SetDelimiter(const Value: char); -begin - FDelimiter := Value; - if Value > #0 then - FOptions := FOptions + [coDelimiter]; -end; - -procedure TAbstractCopyObject.SetEncoding(const Value: string); -begin - FEncoding := Trim(Value); -end; - -procedure TAbstractCopyObject.SetEscape(const Value: char); -begin - FEscape := Value; - if Value > #0 then - FOptions := FOptions + [coEscape]; -end; - -procedure TAbstractCopyObject.SetForcedColumns(const Value: TStrings); -begin - if Assigned(Value) then FForcedColumns.Assign(Value); -end; - -procedure TAbstractCopyObject.SetDataFormat(const Value: TCopyFormat); -begin - if GetDataFormat <> Value then - case Value of - cfText: FOptions := FOptions - [coBinary, coCSV]; - cfCSV: FOptions := FOptions - [coBinary] + [coCSV]; - cfBinary: FOptions := FOptions - [coDelimiter, coNULL, coCSV] + [coBinary]; - end; -end; - -procedure TAbstractCopyObject.SetNullValue(const Value: string); -begin - FNullValue := Value; -end; - -procedure TAbstractCopyObject.SetOptions(const Value: TCopyOptions); -begin - if (coBinary in Value) and (Value * [coDelimiter, coNULL, coCSV] <> []) then - raise EPSQLCopyException.Create('You cannot specify the coDelimiter, coNULL or coCSV options in binary mode.') - else - FOptions := Value; -end; - -procedure TAbstractCopyObject.SetQuote(const Value: char); -begin - FQuote := Value; - if Value > #0 then - FOptions := FOptions + [coQuote]; -end; - -procedure TAbstractCopyObject.SetSQL(const Value: TStrings); -begin - if Assigned(Value) then FSQL.Assign(Value); -end; - -end. - +{$I pSQLDAC.inc} + +unit PSQLCopy; + +{SVN revision: $Id$} + +interface + +uses {$IFDEF FPC}LCLIntf,{$ENDIF} SysUtils, Classes, PSQLTypes, + PSQLAccess, PSQLDbTables; + +type + + TCopyDirection = (cdIn, cdOut); //in = put, out = get + + TCopyMode = (cmFile, cmSTDInOut, cmProgram); //copy to file, to active connection or to process launched by server + + TCopyFormat = (cfText, cfCSV, cfBinary); + + EPSQLCopyException = class(Exception); + + TCopyOption = (coUseOIDs, coBinary, coCSV, coHeader, coNULL, coDelimiter, coQuote, coEscape, coFreeze); + TCopyOptions = set of TCopyOption; + + TAbstractCopyObject = class(TComponent) + private + FColumns: TStrings; + FDelimiter: char; + FNullValue: string; + FFileName: string; + FCommandLine: string; + FTableName: TFileName; + FDatabase: TPSQLDatabase; + FOptions: TCopyOptions; + FForcedColumns: TStrings; + FSQL: TStrings; + FEscape: char; + FQuote: char; + FRowsAffected: Integer; + FEncoding: string; + procedure SetColumns(const Value: TStrings); + procedure SetDatabase(const Value: TPSQLDatabase); + procedure SetOptions(const Value: TCopyOptions); + procedure SetForcedColumns(const Value: TStrings); + procedure SetSQL(const Value: TStrings); + procedure SetEscape(const Value: char); + procedure SetQuote(const Value: char); + procedure SetDelimiter(const Value: char); + procedure SetNullValue(const Value: string); + function GetDataFormat: TCopyFormat; + procedure SetDataFormat(const Value: TCopyFormat); + procedure SetEncoding(const Value: string); + protected + function GetSQLStatement: string; virtual; abstract; + procedure DoServerSideCopyGet; virtual; abstract; + procedure DoClientSideCopyGet(Stream: TStream); virtual; abstract; + procedure DoServerSideCopyPut; virtual; abstract; + procedure DoClientSideCopyPut(Stream: TStream); virtual; abstract; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + property Delimiter: char read FDelimiter write SetDelimiter; + property Quote: char read FQuote write SetQuote; + property Escape: char read FEscape write SetEscape; + property NullValue: string read FNullValue write SetNullValue; + property Tablename: TFileName read FTablename write FTablename; + property Columns: TStrings read FColumns write SetColumns; + property ForcedColumns: TStrings read FForcedColumns write SetForcedColumns; + property Database: TPSQLDatabase read FDatabase write SetDatabase; + property Options: TCopyOptions read FOptions write SetOptions default []; + property SQL: TStrings read FSQL write SetSQL; + property RowsAffected: Integer read FRowsAffected; + property DataFormat: TCopyFormat read GetDataFormat write SetDataFormat; + property Encoding: string read FEncoding write SetEncoding; + end; + + TCustomPSQLCopy = class(TAbstractCopyObject) + protected + FCopyDirection: TCopyDirection; + FCopyMode: TCopyMode; + FAfterCopyGet: TNotifyEvent; + FBeforeCopyGet: TNotifyEvent; + FAfterCopyPut: TNotifyEvent; + FBeforeCopyPut: TNotifyEvent; + function GetCommaSeparatedText(const AStrings: TStrings):string; + function GetSQLStatement: string; override; + procedure DoServerSideCopyGet; override; + procedure DoClientSideCopyGet(Stream: TStream); override; + procedure DoServerSideCopyPut; override; + procedure DoClientSideCopyPut(Stream: TStream); override; + public + procedure LoadFromStream(Stream: TStream); + procedure SaveToStream(Stream: TStream); + + procedure LoadFromStrings(Strings: TStrings); + procedure SaveToStrings(Strings: TStrings); overload; +{$IFDEF DELPHI_15} + procedure SaveToStrings(Strings: TStrings; Encoding: TEncoding); overload; +{$ENDIF} + + procedure LoadFromClientSideFile(const FileName: string); + procedure SaveToClientSideFile(const FileName: string); + + procedure LoadFromServerSideFile(const FileName: string); + procedure SaveToServerSideFile(const FileName: string); + + procedure LoadFromProgram(const CommandLine: string); + procedure SaveToProgram(const CommandLine: string); + + property BeforeCopyGet: TNotifyEvent read FBeforeCopyGet write FBeforeCopyGet; + property AfterCopyGet: TNotifyEvent read FAfterCopyGet write FAfterCopyGet; + property AfterCopyPut: TNotifyEvent read FAfterCopyPut write FAfterCopyPut; + property BeforeCopyPut: TNotifyEvent read FBeforeCopyPut write FBeforeCopyPut; + end; + + + TPSQLCopy = class(TCustomPSQLCopy) + published + property Delimiter; + property Quote; + property Escape; + property NullValue; + property Tablename; + property Columns; + property ForcedColumns; + property Database; + property Options; + property SQL; + property DataFormat; + property Encoding; + property BeforeCopyGet; + property AfterCopyGet; + property BeforeCopyPut; + property AfterCopyPut; + end; + +implementation + +uses DB{$IFNDEF DELPHI_5}, StrUtils{$ENDIF}; + +{ TAbstractCopyObject } + +constructor TAbstractCopyObject.Create(AOwner: TComponent); +var I: integer; +begin + inherited; + FColumns := TStringList.Create; + FForcedColumns := TStringList.Create; + FSQL := TStringList.Create; + FDelimiter := #0; + FNullValue := ''; + FTableName := ''; + if (csDesigning in ComponentState) and Assigned(AOwner) and (DBList.Count > 0) then + begin + for I := DBList.Count - 1 downto 0 do + if TCustomConnection(DBList[I]).Owner = AOwner then + begin + Database := TPSQLDatabase(DBList[I]); + Break; + end; + if not Assigned(Database) then + Database := TPSQLDatabase(DBList[DBList.Count - 1]); + end; +end; + +destructor TAbstractCopyObject.Destroy; +begin + FColumns.Free; + FForcedColumns.Free; + FSQL.Free; + inherited Destroy; +end; + +function TAbstractCopyObject.GetDataFormat: TCopyFormat; +begin + if coCSV in FOptions then + Result := cfCSV + else + if coBinary in FOptions then + Result := cfBinary + else + Result := cfText; +end; + +procedure TAbstractCopyObject.SetColumns(const Value: TStrings); +begin + if Assigned(Value) then FColumns.Assign(Value); +end; + +procedure TAbstractCopyObject.SetDatabase(const Value: TPSQLDatabase); +begin + if (Value <> FDatabase) then FDatabase := Value; +end; + +procedure TCustomPSQLCopy.DoClientSideCopyGet(Stream: TStream); +var Result: PPGresult; + LineRes: integer; + Buffer: PAnsiDACChar; + S: DACAString; + AConnect: TNativeConnect; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + if Assigned(FBeforeCopyGet) then + FBeforeCopyGet(Self); + FRowsAffected := -1; + if not FDatabase.Connected then DatabaseError(SDatabaseClosed); + AConnect := TNativeConnect(FDatabase.Handle); + Result := _PQExecute(AConnect, GetSQLStatement); + try + Stream.Position := 0; + Stream.Size := 0; + try + if PQresultStatus(Result) = PGRES_COPY_OUT then + begin + Repeat + LineRes := PQgetCopyData(AConnect.Handle, @Buffer); + if (LineRes > 0) and Assigned(Buffer) then + begin + S := {$IFDEF NEXTGEN}string{$ENDIF}(Copy(Buffer,1,LineRes)); + Stream.Write(Pointer( + {$IFNDEF NEXTGEN}S{$ELSE}M.AsAnsi(S).ToPointer{$ENDIF} + )^,length(S)); + end; + if Buffer <> nil then + PQfreemem (Buffer); + Until LineRes < 0; + if PQresultStatus(Result) <> PGRES_COMMAND_OK then + AConnect.CheckResult; + if Assigned(FAfterCopyGet) then + FAfterCopyGet(Self); + FRowsAffected := StrToIntDef(String(PQcmdTuples(Result)), -1); + end + else + AConnect.CheckResult; + except + on E: EPSQLException do + raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); + else + raise; + end; + finally + PQClear(Result); + end; +end; + +procedure TCustomPSQLCopy.DoClientSideCopyPut(Stream: TStream); +var Result, Result2: PPGresult; + Count: cardinal; + Buffer: array[Word] of AnsiDACByteChar; + AConnect: TNativeConnect; +begin + if Assigned(FBeforeCopyPut) then + FBeforeCopyPut(Self); + FRowsAffected := -1; + if not FDatabase.Connected then DatabaseError(SDatabaseClosed); + AConnect := TNativeConnect(FDatabase.Handle); + Result := _PQExecute(AConnect, GetSQLStatement); + try + try + Stream.Position := 0; + if PQresultStatus(Result) = PGRES_COPY_IN then + begin + Count := Stream.Read(Buffer[0], Length(Buffer)); + while Count > 0 do + if PQputCopyData(AConnect.Handle, {$IFNDEF NEXTGEN} + Buffer + {$ELSE} + @Buffer + {$ENDIF}, + Count) <= 0 then + AConnect.CheckResult(Result) + else + Count := Stream.Read(Buffer,Length(Buffer)); + if PQputCopyEnd(AConnect.Handle) <= 0 then + AConnect.CheckResult(Result) + else + begin + Result2 := PQgetResult(AConnect.Handle); + try + AConnect.CheckResult(Result2); + FRowsAffected := StrToIntDef(String(PQcmdTuples(Result2)), -1); + finally + PQClear(Result2); + end; + end; + if Assigned(FAfterCopyPut) then + FAfterCopyPut(Self); + end + else + AConnect.CheckResult(Result); + finally + PQClear(Result); + end; + except + on E: EPSQLException do + raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); + else + raise; + end; +end; + +procedure TCustomPSQLCopy.DoServerSideCopyGet; +var + Result: PPGResult; + AConnect: TNativeConnect; +begin + if Assigned(FBeforeCopyGet) then + FBeforeCopyGet(Self); + FRowsAffected := -1; + if not FDatabase.Connected then DatabaseError(SDatabaseClosed); + AConnect := TNativeConnect(FDatabase.Handle); + Result := _PQExecute(AConnect, GetSQLStatement); + try + try + AConnect.CheckResult(Result); + FRowsAffected := StrToIntDef(String(PQcmdTuples(Result)), -1); + finally + PQClear(Result); + end; + except + on E: EPSQLException do + raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); + else + raise; + end; + if Assigned(FAfterCopyGet) then + FAfterCopyGet(Self); +end; + +procedure TCustomPSQLCopy.DoServerSideCopyPut; + var + Result: PPGResult; + AConnect: TNativeConnect; +begin + if Assigned(FBeforeCopyPut) then + FBeforeCopyPut(Self); + FRowsAffected := -1; + if not FDatabase.Connected then DatabaseError(SDatabaseClosed); + AConnect := TNativeConnect(FDatabase.Handle); + Result := _PQExecute(AConnect, GetSQLStatement); + try + try + AConnect.CheckResult; + FRowsAffected := StrToIntDef(String(PQcmdTuples(Result)), -1); + finally + PQClear(Result); + end; + except + on E: EPSQLException do + raise EPSQLDatabaseError.Create(FDatabase.Engine, FDatabase.Engine.CheckError); + else + raise; + end; + if Assigned(FAfterCopyPut) then + FAfterCopyPut(Self); +end; + +function TCustomPSQLCopy.GetCommaSeparatedText( + const AStrings: TStrings): string; +var i: integer; +begin + if AStrings.Count > 0 then + Result := AStrings[0] + else + begin + Result := ''; + Exit; + end; + for i:=1 to AStrings.Count-1 do + Result := Result + ', ' + AStrings[i]; +end; + +function TCustomPSQLCopy.GetSQLStatement: string; + + function GetOldWithStmt: string; + const + cForced: array[TCopyDirection] of string = (' FORCE NOT NULL %s ',' FORCE QUOTE %s '); + begin + if coUseOIDs in FOptions then Result := Result + 'OIDS '; + if coBinary in FOptions then + Result := Result + 'BINARY ' + else + begin + if (coDelimiter in FOptions) and (FDelimiter > #0) then + Result := Result + Format(' DELIMITER %s ',[QuotedStr(FDelimiter)]); + if (coNull in FOptions) and (FNullValue > '') then + Result := Result + Format(' NULL %s ',[QuotedStr(FNullValue)]); + if (coCSV in FOptions) then + begin + Result := Result + ' CSV '; + if (coHeader in FOptions) then + Result := Result + ' HEADER '; + if (coQuote in FOptions) and (FQuote > #0) then + Result := Result + Format(' QUOTE %s ',[FQuote]); + if (coEscape in FOptions) and (FEscape > #0) then + Result := Result + Format(' ESCAPE %s ',[FEscape]); + if FForcedColumns.Count > 0 then + Result := Result + Format(cForced[FCopyDirection],[GetCommaSeparatedText(FForcedColumns)]); + end; + end; + if Result > '' then + Result := ' WITH ' + Result; + end; + + function GetNewWithStmt: string; + const + cFormat: array[TCopyFormat] of string = ('''text''', '''csv''', '''binary'''); + cForced: array[TCopyDirection] of string = (', FORCE_NOT_NULL %s', ', FORCE_QUOTE %s'); + begin + Result := 'FORMAT ' + cFormat[GetDataFormat]; + if coUseOIDs in FOptions then Result := Result + ', OIDS true'; + if coFreeze in FOptions then Result := Result + ', FREEZE true'; + + if not (coBinary in FOptions) then + begin + if (coDelimiter in FOptions) and (FDelimiter > #0) then + Result := Result + Format(', DELIMITER %s ',[QuotedStr(FDelimiter)]); + if (coNull in FOptions) and (FNullValue > '') then + Result := Result + Format(', NULL %s ',[QuotedStr(FNullValue)]); + if (coCSV in FOptions) then + begin + if (coHeader in FOptions) then + Result := Result + ', HEADER true'; + if (coQuote in FOptions) and (FQuote > #0) then + Result := Result + Format(', QUOTE %s ',[QuotedStr(FQuote)]); + if (coEscape in FOptions) and (FEscape > #0) then + Result := Result + Format(', ESCAPE %s ',[QuotedStr(FEscape)]); + if FForcedColumns.Count > 0 then + Result := Result + Format(cForced[FCopyDirection],[GetCommaSeparatedText(FForcedColumns)]); + end; + end; + if FEncoding > '' then + Result := Result + ', ENCODING ' + QuotedStr(FEncoding); + Result := ' WITH (' + Result + ')'; + end; + +begin + if (FCopyDirection = cdOUT) and (FSQL.Count > 0) then + Result := Format('COPY (%s) ',[Trim(FSQL.Text)]) + else + begin + Result := Format('COPY %s ',[FTablename]); + if FColumns.Count > 0 then + Result := Result + '(' + GetCommaSeparatedText(FColumns) + ')'; + end; + Result := Result + ifthen(FCopyDirection = cdOut, ' TO ', ' FROM '); + case FCopyMode of + cmFile: + begin + if FFileName = '' then + if FCopyDirection = cdIn then + raise EPSQLCopyException.Create('FileName is required for putting data.') + else + FFilename := DateTimeToStr(Date) + '.pge'; + Result := Result + QuotedStr(FFileName); + end; + cmSTDInOut: + Result := Result + ifthen(FCopyDirection = cdOUT, 'STDOUT', 'STDIN'); + cmProgram: + Result := Result + 'PROGRAM ' + QuotedStr(FCommandLine); + end; + + if Assigned(FDatabase) then + Result := Result + ifthen(FDatabase.ServerVersionAsInt < 090000, GetOldWithStmt(), GetNewWithStmt()); +end; + + +procedure TCustomPSQLCopy.LoadFromClientSideFile(const FileName: string); +var FS: TFileStream; +begin + FS := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(FS); + finally + FS.Free; + end; +end; + +procedure TCustomPSQLCopy.LoadFromProgram(const CommandLine: string); +begin + FCopyDirection := cdIN; + FCopyMode := cmProgram; + FCommandLine := CommandLine; + DoServerSideCopyPut(); +end; + +procedure TCustomPSQLCopy.LoadFromServerSideFile(const FileName: string); +begin + FCopyDirection := cdIN; + FCopyMode := cmSTDINOUT; + FFileName := FileName; + DoServerSideCopyPut(); +end; + +procedure TCustomPSQLCopy.LoadFromStream(Stream: TStream); +begin + FCopyDirection := cdIN; + FCopyMode := cmSTDINOUT; + if not Assigned(Stream) then Exit; + DoClientSideCopyPut(Stream); +end; + +procedure TCustomPSQLCopy.LoadFromStrings(Strings: TStrings); +var MS: TMemoryStream; +begin + MS := TMemoryStream.Create; + try + Strings.SaveToStream(MS); + LoadFromStream(MS); + finally + MS.Free; + end; +end; + +procedure TCustomPSQLCopy.SaveToClientSideFile(const FileName: string); +var FS: TFileStream; +begin + FS := TFileStream.Create(FileName, fmCreate or fmShareExclusive); + try + SaveToStream(FS); + finally + FS.Free; + end; +end; + +procedure TCustomPSQLCopy.SaveToProgram(const CommandLine: string); +begin + FCopyMode := cmProgram; + FCopyDirection := cdOUT; + FCommandLine := CommandLine; + DoServerSideCopyGet(); +end; + +procedure TCustomPSQLCopy.SaveToServerSideFile(const FileName: string); +begin + FCopyMode := cmFile; + FCopyDirection := cdOut; + FFileName := FileName; + DoServerSideCopyGet(); +end; + +procedure TCustomPSQLCopy.SaveToStream(Stream: TStream); +begin + FCopyDirection := cdOUT; + FCopyMode := cmSTDINOUT; + if not Assigned(Stream) then + raise EPSQLCopyException.Create('Stream not assigned'); + DoClientSideCopyGet(Stream); +end; + +{$IFDEF DELPHI_15} +procedure TCustomPSQLCopy.SaveToStrings(Strings: TStrings; Encoding: TEncoding); +var MS: TMemoryStream; +begin + if Assigned(Strings) then + begin + MS := TMemoryStream.Create; + try + SaveToStream(MS); + MS.Position := 0; + Strings.LoadFromStream(MS, Encoding); + finally + MS.Free; + end; + end; +end; +{$ENDIF} + +procedure TCustomPSQLCopy.SaveToStrings(Strings: TStrings); +var MS: TMemoryStream; +begin + if Assigned(Strings) then + begin + MS := TMemoryStream.Create; + try + SaveToStream(MS); + MS.Position := 0; + Strings.LoadFromStream(MS); + finally + MS.Free; + end; + end; +end; + + +procedure TAbstractCopyObject.SetDelimiter(const Value: char); +begin + FDelimiter := Value; + if Value > #0 then + FOptions := FOptions + [coDelimiter]; +end; + +procedure TAbstractCopyObject.SetEncoding(const Value: string); +begin + FEncoding := Trim(Value); +end; + +procedure TAbstractCopyObject.SetEscape(const Value: char); +begin + FEscape := Value; + if Value > #0 then + FOptions := FOptions + [coEscape]; +end; + +procedure TAbstractCopyObject.SetForcedColumns(const Value: TStrings); +begin + if Assigned(Value) then FForcedColumns.Assign(Value); +end; + +procedure TAbstractCopyObject.SetDataFormat(const Value: TCopyFormat); +begin + if GetDataFormat <> Value then + case Value of + cfText: FOptions := FOptions - [coBinary, coCSV]; + cfCSV: FOptions := FOptions - [coBinary] + [coCSV]; + cfBinary: FOptions := FOptions - [coDelimiter, coNULL, coCSV] + [coBinary]; + end; +end; + +procedure TAbstractCopyObject.SetNullValue(const Value: string); +begin + FNullValue := Value; +end; + +procedure TAbstractCopyObject.SetOptions(const Value: TCopyOptions); +begin + if (coBinary in Value) and (Value * [coDelimiter, coNULL, coCSV] <> []) then + raise EPSQLCopyException.Create('You cannot specify the coDelimiter, coNULL or coCSV options in binary mode.') + else + FOptions := Value; +end; + +procedure TAbstractCopyObject.SetQuote(const Value: char); +begin + FQuote := Value; + if Value > #0 then + FOptions := FOptions + [coQuote]; +end; + +procedure TAbstractCopyObject.SetSQL(const Value: TStrings); +begin + if Assigned(Value) then FSQL.Assign(Value); +end; + +end. + diff --git a/PSQLDAC.inc b/Source/PSQLDAC.inc similarity index 95% rename from PSQLDAC.inc rename to Source/PSQLDAC.inc index fc34a5d..59af5ec 100644 --- a/PSQLDAC.inc +++ b/Source/PSQLDAC.inc @@ -1,393 +1,393 @@ -{SVN revision: $Id$} - -{.$DEFINE M_DEBUG}//remove "." to go into M_DEBUG mode - //don't do this until you exactly know what you are doing - -{$IFDEF M_DEBUG} - {$D+} {$L+} - {$HINTS ON} - {$WARNINGS ON} -{$ENDIF} - -//--------------------------- -// FreePascal/Lazarus -//--------------------------- -// -{$IFDEF FPC} - {$MODE DELPHI} -{$ENDIF} - -{$IFDEF ANDROID} {$DEFINE MOBILE} {$ENDIF} -{$IFDEF IOS} {$DEFINE MOBILE} {$ENDIF} -{$IFDEF LINUX} {$DEFINE MOBILE} {$ENDIF} - - -{$UNDEF MACOS_OR_MOBILE} -{$IFDEF MACOS}{$DEFINE MACOS_OR_MOBILE}{$ENDIF} -{$IFDEF MOBILE}{$DEFINE MACOS_OR_MOBILE}{$ENDIF} - -//--------------------------- -// Delphi/Builder versions -//--------------------------- -//DELPHI 28 (RX11 Alexandria} -{$IFDEF VER350} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_28} - {$DEFINE DELPHI_27} - {$DEFINE DELPHI_26} - {$DEFINE DELPHI_25} - {$DEFINE DELPHI_24} - {$DEFINE DELPHI_23} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 27 (RX10.4 Sydney} -{$IFDEF VER340} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_27} - {$DEFINE DELPHI_26} - {$DEFINE DELPHI_25} - {$DEFINE DELPHI_24} - {$DEFINE DELPHI_23} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 26 (RX10.3 Rio} -{$IFDEF VER330} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_26} - {$DEFINE DELPHI_25} - {$DEFINE DELPHI_24} - {$DEFINE DELPHI_23} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 25 (RX10.2 Tokyo} -{$IFDEF VER320} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_25} - {$DEFINE DELPHI_24} - {$DEFINE DELPHI_23} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 24 (RX10.1 Big Ben) -{$IFDEF VER310} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_24} - {$DEFINE DELPHI_23} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 23 (RX10 Seattle) -{$IFDEF VER300} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_23} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 22 (Delphi XE7 Elbrus) -{$IFDEF VER290} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_22} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 21 (Delphi XE7 Proteus) -{$IFDEF VER280} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_21} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 20 (Delphi XE6 Proteus) -{$IFDEF VER270} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_20} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 19 (Delphi XE5 Zephyr) -{$IFDEF VER260} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_19} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 18 (Delphi XE4) -{$IFDEF VER250} - {$UNDEF NEXTGEN} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_18} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 17 (Delphi XE3) -{$IFDEF VER240} - {$UNDEF NEXTGEN} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_17} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 16 (Delphi XE2) -{$IFDEF VER230} - {$DEFINE FMX_AVAILABLE} - {$DEFINE DELPHI_16} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 15 (Delphi XE) -{$IFDEF VER220} - {$DEFINE DELPHI_15} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 14 (Delphi Weaver) -{$IFDEF VER210} - {$DEFINE DELPHI_14} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 12 (2009, Tiburon) -{$IFDEF VER200} - {$DEFINE DELPHI_12} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -{$IFNDEF DELPHI_12} - {$DEFINE UNDER_DELPHI_12} -{$ENDIF} - -//DELPHI 11(2007) -{$IFDEF VER185} - {$DEFINE DELPHI_11} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} - {$WARN SYMBOL_DEPRECATED OFF} -{$ENDIF} - -//DELPHI 10 -{$IFDEF VER180} - {$DEFINE DELPHI_10} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 9 -{$IFDEF VER170} - {$DEFINE DELPHI_9} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 7 -{$IFDEF VER150} - {$DEFINE DELPHI_7} - {$DEFINE DELPHI_6} -{$ENDIF} - -//DELPHI 6 -{$IFDEF VER140} - {$DEFINE DELPHI_6} - {$DEFINE UNDER_DELPHI_6} -{$ENDIF} - -//DELPHI 5 and C++BUILDER 5 -{$IFDEF VER130} - {$DEFINE DELPHI_5} - {$DEFINE UNDER_DELPHI_6} - {$DEFINE MSWINDOWS} -{$ENDIF} - -//DELPHI 4 -{$IFDEF VER120} - {$DEFINE DELPHI_4} -{$ENDIF} - -//C++BUILDER 4 -{$IFDEF VER125} - {$DEFINE DELPHI_4} -{$ENDIF} - -{$IFDEF DELPHI_7} - {$WARN UNSAFE_CODE OFF} - {$WARN UNSAFE_TYPE OFF} - {$WARN UNSAFE_CAST OFF} -{$ENDIF} - -{$IFDEF BCB} - {$ObjExportAll On} -{$ENDIF} - +{SVN revision: $Id$} + +{.$DEFINE M_DEBUG}//remove "." to go into M_DEBUG mode + //don't do this until you exactly know what you are doing + +{$IFDEF M_DEBUG} + {$D+} {$L+} + {$HINTS ON} + {$WARNINGS ON} +{$ENDIF} + +//--------------------------- +// FreePascal/Lazarus +//--------------------------- +// +{$IFDEF FPC} + {$MODE DELPHI} +{$ENDIF} + +{$IFDEF ANDROID} {$DEFINE MOBILE} {$ENDIF} +{$IFDEF IOS} {$DEFINE MOBILE} {$ENDIF} +{$IFDEF LINUX} {$DEFINE MOBILE} {$ENDIF} + + +{$UNDEF MACOS_OR_MOBILE} +{$IFDEF MACOS}{$DEFINE MACOS_OR_MOBILE}{$ENDIF} +{$IFDEF MOBILE}{$DEFINE MACOS_OR_MOBILE}{$ENDIF} + +//--------------------------- +// Delphi/Builder versions +//--------------------------- +//DELPHI 28 (RX11 Alexandria} +{$IFDEF VER350} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_28} + {$DEFINE DELPHI_27} + {$DEFINE DELPHI_26} + {$DEFINE DELPHI_25} + {$DEFINE DELPHI_24} + {$DEFINE DELPHI_23} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 27 (RX10.4 Sydney} +{$IFDEF VER340} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_27} + {$DEFINE DELPHI_26} + {$DEFINE DELPHI_25} + {$DEFINE DELPHI_24} + {$DEFINE DELPHI_23} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 26 (RX10.3 Rio} +{$IFDEF VER330} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_26} + {$DEFINE DELPHI_25} + {$DEFINE DELPHI_24} + {$DEFINE DELPHI_23} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 25 (RX10.2 Tokyo} +{$IFDEF VER320} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_25} + {$DEFINE DELPHI_24} + {$DEFINE DELPHI_23} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 24 (RX10.1 Big Ben) +{$IFDEF VER310} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_24} + {$DEFINE DELPHI_23} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 23 (RX10 Seattle) +{$IFDEF VER300} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_23} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 22 (Delphi XE7 Elbrus) +{$IFDEF VER290} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_22} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 21 (Delphi XE7 Proteus) +{$IFDEF VER280} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_21} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 20 (Delphi XE6 Proteus) +{$IFDEF VER270} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_20} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 19 (Delphi XE5 Zephyr) +{$IFDEF VER260} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_19} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 18 (Delphi XE4) +{$IFDEF VER250} + {$UNDEF NEXTGEN} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_18} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 17 (Delphi XE3) +{$IFDEF VER240} + {$UNDEF NEXTGEN} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_17} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 16 (Delphi XE2) +{$IFDEF VER230} + {$DEFINE FMX_AVAILABLE} + {$DEFINE DELPHI_16} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 15 (Delphi XE) +{$IFDEF VER220} + {$DEFINE DELPHI_15} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 14 (Delphi Weaver) +{$IFDEF VER210} + {$DEFINE DELPHI_14} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 12 (2009, Tiburon) +{$IFDEF VER200} + {$DEFINE DELPHI_12} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +{$IFNDEF DELPHI_12} + {$DEFINE UNDER_DELPHI_12} +{$ENDIF} + +//DELPHI 11(2007) +{$IFDEF VER185} + {$DEFINE DELPHI_11} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} + {$WARN SYMBOL_DEPRECATED OFF} +{$ENDIF} + +//DELPHI 10 +{$IFDEF VER180} + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 9 +{$IFDEF VER170} + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 7 +{$IFDEF VER150} + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_6} +{$ENDIF} + +//DELPHI 6 +{$IFDEF VER140} + {$DEFINE DELPHI_6} + {$DEFINE UNDER_DELPHI_6} +{$ENDIF} + +//DELPHI 5 and C++BUILDER 5 +{$IFDEF VER130} + {$DEFINE DELPHI_5} + {$DEFINE UNDER_DELPHI_6} + {$DEFINE MSWINDOWS} +{$ENDIF} + +//DELPHI 4 +{$IFDEF VER120} + {$DEFINE DELPHI_4} +{$ENDIF} + +//C++BUILDER 4 +{$IFDEF VER125} + {$DEFINE DELPHI_4} +{$ENDIF} + +{$IFDEF DELPHI_7} + {$WARN UNSAFE_CODE OFF} + {$WARN UNSAFE_TYPE OFF} + {$WARN UNSAFE_CAST OFF} +{$ENDIF} + +{$IFDEF BCB} + {$ObjExportAll On} +{$ENDIF} + diff --git a/PSQLDbTables.pas b/Source/PSQLDbTables.pas similarity index 96% rename from PSQLDbTables.pas rename to Source/PSQLDbTables.pas index 8a71ffb..31ec986 100644 --- a/PSQLDbTables.pas +++ b/Source/PSQLDbTables.pas @@ -1,7666 +1,7666 @@ -{$I PSQLDAC.inc} -unit PSQLDbTables; - -{SVN revision: $Id$} - -{$R-,T-,H+,X+} -{$C+} -interface - -uses SysUtils, Classes, Db, - {$IFDEF DELPHI_9}DbCommon{$ELSE}PSQLCommon{$ENDIF}, - {$IFDEF DELPHI_6}Variants,{$ENDIF} - {$IFDEF FPC}Variants,{$ENDIF} - {$IFDEF NEXTGEN}Generics.Collections,{$ENDIF} - {$IFDEF DELPHI_17}System.Types, System.Generics.Collections,{$ENDIF} - PSQLAccess, PSQLTypes; - -const - VERSION : string = '3.12'; - -{ TDBDataSet flags } - dbfOpened = 0; - dbfPrepared = 1; - dbfExecSQL = 2; - dbfTable = 3; - dbfFieldList = 4; - dbfIndexList = 5; - dbfStoredProc = 6; - dbfExecProc = 7; - dbfProcDesc = 8; - dbfDatabase = 9; - dbfProvider = 10; - -{ FieldType Mappings } - -const - {$IFDEF FPC} - FldTypeMap: TFieldMap = ( - fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, //0..4 - fldBOOL, fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, //5..11 - fldBYTES, fldVARBYTES, fldINT32, fldBLOB, fldBLOB, fldBLOB, fldBLOB, //12..18 - fldBLOB, fldBLOB, fldBLOB, fldCURSOR, fldZSTRING, fldZSTRING, //19..24 - fldINT64, fldADT, fldArray, fldREF, fldTABLE, fldBLOB, fldBLOB, //25..31 - fldUNKNOWN, fldUNKNOWN, fldUNKNOWN, fldZSTRING, fldDATETIME, fldBCD, - fldZSTRING, fldBLOB); - - FldSubTypeMap: array[TFieldType] of Word = ( - 0, 0, 0, 0, 0, 0, 0, fldstMONEY, 0, 0, 0, 0, 0, 0, fldstAUTOINC, - fldstBINARY, fldstMEMO, fldstGRAPHIC, fldstFMTMEMO, fldstOLEOBJ, - fldstDBSOLEOBJ, fldstTYPEDBINARY, 0, fldstFIXED, fldstUNICODE, - 0, 0, 0, 0, 0, fldstHBINARY, fldstHMEMO, 0, 0, 0, 0, 0, 0, - fldstFIXED, fldstMEMO); - - {$ELSE} - - FldTypeMap: TFieldMap = ( - fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, //0..4 - fldBOOL, fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, //5..11 - fldBYTES, fldVARBYTES, fldINT32, fldBLOB, fldBLOB, fldBLOB, fldBLOB, //12..18 - fldBLOB, fldBLOB, fldBLOB, fldCURSOR, fldZSTRING, fldZSTRING, //19..24 - fldINT64, fldADT, fldArray, fldREF, fldTABLE, fldBLOB, fldBLOB, //25..31 - fldUNKNOWN, fldUNKNOWN, fldUNKNOWN, fldZSTRING - {$IFDEF DELPHI_6}, fldDATETIME, fldBCD{$ENDIF} //26..37 - {$IFDEF DELPHI_10}, fldZSTRING, fldBLOB, fldDATETIME, fldINT32{$ENDIF} //38..41 - {$IFDEF DELPHI_12}, fldINT32, fldINT16, fldUNKNOWN, fldFLOATIEEE, fldUNKNOWN, fldUNKNOWN, fldUNKNOWN{$ENDIF} //42..48 - {$IFDEF DELPHI_14}, fldUNKNOWN, fldUNKNOWN, fldUNKNOWN{$ENDIF} //49..52 - ); - - FldSubTypeMap: array[TFieldType] of Word = ( - 0, 0, 0, 0, 0, 0, 0, fldstMONEY, 0, 0, 0, 0, 0, 0, fldstAUTOINC, - fldstBINARY, fldstMEMO, fldstGRAPHIC, fldstFMTMEMO, fldstOLEOBJ, - fldstDBSOLEOBJ, fldstTYPEDBINARY, 0, fldstFIXED, fldstUNICODE, - 0, 0, 0, 0, 0, fldstHBINARY, fldstHMEMO, 0, 0, 0, 0{$IFDEF DELPHI_6} , 0, 0{$ENDIF} - {$IFDEF DELPHI_10}, fldstFIXED, fldstMEMO, 0, 0 {$ENDIF} //38..41 - {$IFDEF DELPHI_12}, 0, 0, 0, 0, 0, 0, 0{$ENDIF} //42..48 - {$IFDEF DELPHI_14}, 0, 0, 0{$ENDIF} //49..52 - ); - {$ENDIF} - - - DataTypeMap: array[0..MAXLOGFLDTYPES - 1] of TFieldType = ( - ftUnknown, ftString, ftDate, ftBlob, ftBoolean, ftSmallint, - ftInteger, ftFloat, ftBCD, ftBytes, ftTime, ftDateTime, - ftWord, ftInteger, ftUnknown, ftVarBytes, ftUnknown, ftUnknown, - ftLargeInt, ftLargeInt, ftADT, ftArray, ftReference, ftDataSet - {$IFDEF DELPHI_6},ftTimeStamp{$ENDIF} - {$IFDEF DELPHI_12},ftFMTBcd, ftWideString{$ENDIF}); - - BlobTypeMap: array[fldstMEMO..fldstBFILE] of TFieldType = ( - ftMemo, ftBlob, ftFmtMemo, ftParadoxOle, ftGraphic, ftDBaseOle, - ftTypedBinary, ftBlob, ftBlob, ftBlob, ftBlob, ftOraClob, ftOraBlob, - ftBlob, ftBlob); - -type - - //used for LOCK TABLE - TPSQLLockType = (ltAccessShare, ltRowShare, ltRowExclusive, ltShareUpdateExclusive, - ltShare, ltShareRowExclusive, ltExclusive, ltAccessExclusive); - - //used for DataEvent handlers - TDataEventInfo = - {$IFDEF FPC}Ptrint{$ELSE} - {$IFDEF DELPHI_16}NativeInt - {$ELSE}LongInt - {$ENDIF} - {$ENDIF}; - - { Forward declarations } - TPSQLDatabase = Class; - TPSQLDatabaseClass = Class of TPSQLDatabase; - TPSQLDataSet = Class; - TPSQLTable = Class; - TPSQLTableClass = Class of TPSQLTable; - TPSQLQuery = Class; - TPSQLQueryClass = Class of TPSQLQuery; - - { Exception Classes } - EPSQLDatabaseError = Class(EDatabaseError) - private - FErrorCode: Word; - FErrorPos:string; - FErrorContext:string; - FErrorseverity:string; - FErrorsqlstate:string; - FErrorprimary:string; - FErrordetail:string; - FErrorhint:string; - FErrorinternalpos:string; - FErrorinternalquery:string; - FErrorsourcefile:string; - FErrorsourceline:string; - FErrorsourcefunc:string; - FErrorDataTypeName: string; - FErrorColumnName: string; - FErrorSchemaName: string; - FErrorTableName: string; - FErrorConstraintName: string; - public - constructor Create(Engine : TPSQLEngine; ErrorCode: Word); - destructor Destroy; override; - property ErrorCode: Word read FErrorCode; - property ErrorPos : string read FErrorPos; - property ErrorContext : string read FErrorContext; - property ErrorSeverity : string read FErrorseverity; - property ErrorSqlState : string read FErrorsqlstate; - property ErrorPrimary : string read FErrorprimary; - property ErrorDetail : string read FErrordetail; - property ErrorHint : string read FErrorhint; - property ErrorInternalPos : string read FErrorinternalpos; - property ErrorInternalQuery : string read FErrorinternalquery; - property ErrorSourceFile : string read FErrorsourcefile; - property ErrorSourceLine : string read FErrorsourceline; - property ErrorSourceFunc : string read FErrorsourcefunc; - property ErrorSchemaName : string read FErrorSchemaName; - property ErrorTableName : string read FErrorTableName; - property ErrorColumnName : string read FErrorColumnName; - property ErrorDataTypeName : string read FErrorDataTypeName; - property ErrorConstraintName : string read FErrorConstraintName; - end; - - ENoResultSet = class(EDatabaseError); - -{$IFDEF DELPHI_12} - TPSQLLookupList = class(TLookupList) - private - FList: TList{$IFDEF NEXTGEN}{$ENDIF}; - public - constructor Create; override; - destructor Destroy; override; - procedure Add(const AKey, AValue: Variant); override; - procedure Clear; override; - function ValueOfKey(const AKey: Variant): Variant; override; - end; - - TPSQLFieldDef = class(TFieldDef) - private - FNativeDataType: cardinal; - procedure SetNativeDataType(const Value: cardinal); - published - property NativeDataType: cardinal read FNativeDataType write SetNativeDataType default 0; - end; - - TPSQLFieldDefs = class(TFieldDefs) - protected - function GetFieldDefClass: TFieldDefClass; override; - end; -{$ENDIF} - - TParamClass = class of TParam; - - TPSQLParam = class(TParam) - private - FDataTypeOID: cardinal; - FBinary: boolean; - procedure SetDataTypeOID(const Value: cardinal); - protected - function IsEqual(Value: TParam): Boolean; - published - property DataTypeOID: cardinal read FDataTypeOID write SetDataTypeOID default 0; - property Binary: boolean read FBinary write FBinary default False; - end; - - TPSQLParams = class(TParams) - private - FOwner: TPersistent; - function GetItem(Index: Integer): TPSQLParam; - procedure SetItem(Index: Integer; const Value: TPSQLParam); - protected - function GetOwner: TPersistent; override; - public - constructor Create(Owner: TPersistent); overload; - constructor Create; overload; - procedure AssignValues(Value: TParams); - function CreateParam(FldType: TFieldType; const ParamName: string; - ParamType: TParamType; const DataTypeOID: cardinal = 0; Binary: boolean = False): TPSQLParam; - function ParamByName(const Value: string): TPSQLParam; - function ParseSQL(SQL: string; DoCreate: Boolean): string; reintroduce; - property Items[Index: Integer]: TPSQLParam read GetItem write SetItem; default; - end; - - TDatabaseNoticeEvent = procedure (Sender: TPSQLDatabase; Message: string) of object; - TBaseDatabaseLoginEvent = procedure(Database: TPSQLDatabase; LoginParams: TStrings) of object; - TDbExceptionEvent = procedure (Sender: TObject; E: Exception) of object; - - TDBFlags = set of 0..15; - - TTransIsolation = (tiDirtyRead, tiReadCommitted, tiRepeatableRead, tiSerializable); - - - TPSQLDBDesignOption = (ddoStoreConnected, ddoStorePassword); - TPSQLDBDesignOptions = set of TPSQLDBDesignOption; - - IPSQLLoginDialog = interface - ['{A5CDBD6A-3212-47EA-9CC0-04C11AE11FC7}'] - function Execute(const DB: TPSQLDatabase): boolean; - end; - - TPSQLDatabase = Class(TCustomConnection) - private - FAbout : TPSQLDACAbout; - FTransIsolation: TTransIsolation; - FKeepConnection: Boolean; //AutoStop - FOEMConvert : Boolean; //OEM->ANSI - FCharSet: string; - FCommandTimeout: cardinal; - FEngine : TPSQLEngine; //Postgres Engine - FTemporary: Boolean; - FAcquiredHandle: Boolean; - FPseudoIndexes: Boolean; - FHandleShared: Boolean; - FExclusive: Boolean; - FReadOnly: Boolean; - FRefCount: Integer; - FHandle: DAChDBIDb; - FParams: TStrings; - FStmtList: TList{$IFDEF NEXTGEN}{$ENDIF}; - FOwner: string; - FIsTemplate: boolean; - FTablespace: string; - FDatabaseID: cardinal; - FComment: string; - FServerVersion: string; - FDesignOptions: TPSQLDBDesignOptions;//design time info for DB - FOnAdd: TNotifyEvent; - FOnLogin: TBaseDatabaseLoginEvent; - FOnNotice: TDatabaseNoticeEvent; - FNotifyList: TList{$IFDEF NEXTGEN}{$ENDIF}; //List of notify - FDirectQueryList : TList{$IFDEF NEXTGEN}{$ENDIF}; - FCheckIfActiveOnParamChange: boolean; - FSSLMode: TSSLMode; - FErrorVerbosity: TErrorVerbosity; - FOnException: TDbExceptionEvent; - FUseSingleLineConnInfo: boolean; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FLoginDialog: IPSQLLoginDialog; - function GetNotifyItem(Index: Integer): TObject; - function GetNotifyCount: Integer; - procedure FillAddonInfo; - procedure CheckActive; - procedure CheckInactive; - procedure ClearStatements; - procedure EndTransaction(TransEnd: EXEnd); - function GetInTransaction: Boolean; - function GetTransactionStatus: TTransactionStatusType; - function GetServerVersionAsInt: integer; - function GetLibraryVersionAsInt: integer; - procedure Login(LoginParams: TStrings); - procedure ParamsChanging(Sender: TObject); - procedure SetDatabaseFlags; - procedure SetDatabaseName(const Value: string); - procedure SetUserName(const Value: string); - procedure SetUserPassword(const Value: string); - procedure SetServerPort(const Value: Cardinal); - procedure SetConnectionTimeout(const Value: cardinal); - procedure SetCommandTimeout(const Value: cardinal); - procedure SetHost(const Value : string); - procedure SetKeepConnection(Value: Boolean); - procedure SetExclusive(Value: Boolean); - procedure SetHandle(Value: DAChDBIDb); - procedure SetParams(Value: TStrings); - procedure SetReadOnly(Value: Boolean); - procedure SetDummyStr(Value: string); - procedure SetDummyBool(Value: boolean); - procedure SetDummyInt(Value: cardinal); - procedure SetSSLMode(const Value: TSSLMode); - procedure SetErrorVerbosity(const Value: TErrorVerbosity); - function GetDatabaseID: cardinal; - function GetIsTemplate: boolean; - function GetDBOwner: string; - function GetTablespace: string; - function GetIsUnicodeUsed: Boolean; - function GetDatabaseComment: string; - function GetIsSSLUsed: Boolean; - procedure SetUseSSL(Reader: TReader); - function GetUsrName: string; - function GetUserPassword: string; - function GetDatabaseName: string; - function GetSSLOption(Index: integer): string; - procedure SetSSLOption(Index: integer; Value: string); - function GetConnectionTimeout: cardinal; - function GetServerPort: Cardinal; - function GetHost: string; - function GetGUCParamValue(const Name: string): string; - protected - procedure DefineProperties(Filer: TFiler); override; //deal with old missing properties - procedure CloseDatabaseHandle; - procedure CloseDatabase(Database: TPSQLDatabase); - procedure DoConnect; override; - procedure DoDisconnect; override; - function GetConnected: Boolean; override; - function GetStoreConnected: boolean; - function GetDataSet(Index: Integer): TPSQLDataSet; reintroduce; - procedure Loaded; override; - procedure Notification(AComponent: TComponent; Operation: TOperation); override; - procedure InitEngine; //Init SQL Engine - procedure AddDatabase(Value : TPSQLDatabase); - procedure RemoveDatabase(Value : TPSQLDatabase); - property CheckIfActiveOnParamChange: boolean read FCheckIfActiveOnParamChange write FCheckIfActiveOnParamChange; - procedure WriteState(Writer: TWriter); override; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - procedure AssignTo(Dest: TPersistent); override; - - function Engine : TPSQLEngine; - function Execute(const SQL: string; Params: TParams = nil; Cache: Boolean = FALSE; Cursor: phDBICur = nil): Integer; - function GetBackendPID: Integer; - - function SelectString(aSQL: string; var IsOk: boolean; aFieldName: string): string; overload; - function SelectString(aSQL: string; var IsOk: boolean; aFieldNumber: integer = 0): string; overload; - function SelectStringDef(aSQL: string; aDefaultValue: string; aFieldName: string): string; overload; - function SelectStringDef(aSQL: string; aDefaultValue: string; aFieldNumber: integer = 0): string; overload; - - function Ping: TPingStatus; overload; - function Ping(ConnectionParams: TStrings): TPingStatus; overload; - - procedure SelectStrings(aSQL: string; aList: TStrings; aFieldName: string); overload; - procedure SelectStrings(aSQL: string; aList: TStrings; aFieldNumber: integer = 0); overload; - - procedure AddNotify(AItem: TObject); - procedure ApplyUpdates(const DataSets: array of TPSQLDataSet); - procedure CancelBackend(PID : Integer); - procedure CloseDataSets; - procedure CloseNotify; - procedure Commit; - procedure GetCharsets(List: TStrings); - procedure GetDatabases(Pattern: string;List : TStrings); - procedure GetSchemaNames(Pattern: string; SystemSchemas: Boolean; List: TStrings); - procedure GetStoredProcNames(Pattern: string; List: TStrings); - procedure GetTableNames(Pattern: string; SystemTables: Boolean; List: TStrings); - procedure GetTablespaces(Pattern: string; List: TStrings); - procedure GetUserNames(Pattern: string; List: TStrings); - procedure RegisterDirectQuery(aDirectQuery : TObject); - procedure RemoveNotify(AItem: TObject); - procedure Reset; - procedure ReloadGUC; - procedure Rollback; - procedure SetCharSet(CharSet: string); - procedure StartTransaction; - procedure UnregisterDirectQuery(aDirectQuery : TObject); - - procedure Savepoint(const Name: string); - procedure ReleaseSavepoint(const Name: string); - procedure RollbackToSavepoint(const Name: string); - - property DataSets[Index: Integer]: TPSQLDataSet read GetDataSet; - property Handle: DAChDBIDb read FHandle write SetHandle; - property InTransaction: Boolean read GetInTransaction; - property IsUnicodeUsed: Boolean read GetIsUnicodeUsed; - property IsSSLUsed: Boolean read GetIsSSLUsed; - property Notifies[Index: Integer]: TObject read GetNotifyItem; - property NotifyCount: Integer read GetNotifyCount; - property ServerVersionAsInt: integer read GetServerVersionAsInt; - property LibraryVersionAsInt: integer read GetLibraryVersionAsInt; - property Temporary: Boolean read FTemporary write FTemporary; - property TransactionStatus: TTransactionStatusType read GetTransactionStatus; - property UseSingleLineConnInfo: boolean read FUseSingleLineConnInfo write FUseSingleLineConnInfo; - property GUC[const Name: string]: string read GetGUCParamValue; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property LoginDialog: IPSQLLoginDialog read FLoginDialog write FLoginDialog; - property AfterConnect; - property AfterDisconnect; - property BeforeConnect; - property BeforeDisconnect; - property CharSet: string read FCharSet write SetCharSet; - property CommandTimeout: cardinal read FCommandTimeout write SetCommandTimeout default 0; - property Comment: string read GetDatabaseComment write SetDummyStr stored False; - property Connected stored GetStoreConnected; - property DatabaseID: cardinal read GetDatabaseID write SetDummyInt stored False; - property DesignOptions: TPSQLDBDesignOptions read FDesignOptions write FDesignOptions default [ddoStoreConnected, ddoStorePassword]; - property ErrorVerbosity: TErrorVerbosity read FErrorVerbosity write SetErrorVerbosity default evDEFAULT; - property Exclusive: Boolean read FExclusive write SetExclusive default False; - property HandleShared: Boolean read FHandleShared write FHandleShared default False; - property IsTemplate: boolean read GetIsTemplate write SetDummyBool stored False; - property KeepConnection: Boolean read FKeepConnection write SetKeepConnection default True; - property LoginPrompt; - property OEMConvert: Boolean read FOEMConvert write FOEMConvert default False; - property OnAdd: TNotifyEvent read FOnAdd; - property OnException: TDbExceptionEvent read FOnException write FOnException; - property OnLogin: TBaseDatabaseLoginEvent read FOnLogin write FOnLogin; - property OnNotice: TDatabaseNoticeEvent read FOnNotice write FOnNotice; - property Owner: string read GetDBOwner write SetDummyStr stored False; - property ReadOnly: Boolean read FReadOnly write SetReadOnly default FALSE; - property ServerVersion: string read FServerVersion write SetDummyStr stored False; - property Tablespace: string read GetTablespace write SetDummyStr stored False; - property TransIsolation: TTransIsolation read FTransIsolation write FTransIsolation default tiReadCommitted; - //connection parameters - property ConnectionTimeout: cardinal read GetConnectionTimeout write SetConnectionTimeout stored False default 15; - property DatabaseName: string read GetDatabaseName write SetDatabaseName stored False; - property Host: string read GetHost write SetHost stored False; - property Params: TStrings read FParams write SetParams; - property Port : Cardinal read GetServerPort write SetServerPort stored False default PSQL_PORT; - property UserName : string read GetUsrName write SetUserName stored False; //method name changed due to bug in ILINK32 - property UserPassword : string read GetUserPassword write SetUserPassword stored False; - property SSLMode: TSSLMode read FSSLMode write SetSSLMode default sslPrefer; - property SSLCert: string index 0 read GetSSLOption write SetSSLOption stored False; - property SSLKey: string index 1 read GetSSLOption write SetSSLOption stored False; - property SSLRootCert: string index 2 read GetSSLOption write SetSSLOption stored False; - property SSLCRL: string index 3 read GetSSLOption write SetSSLOption stored False; - end; - - { TPSQLBDECallBack } - TPSQLBDECallbackEvent = function(CBInfo: Pointer): CBRType of Object; - - TPSQLBDECallBack = Class(TObject) - Private - FHandle: hDBICur; - FOwner: TObject; - FCBType: CBType; - FOldCBData: Longint; - FOldCBFunc: pfDBICallBack; - FCallbackEvent: TPSQLBDECallbackEvent; - FEngine : TPSQLEngine; - protected - function Invoke(CallType: CBType; CBInfo: Pointer): CBRType; - public - constructor Create(Engine : TPSQLEngine; AOwner: TObject; Handle: hDBICur; CBType: CBType; - CBBuf: Pointer; CBBufSize: Integer; CallbackEvent: TPSQLBDECallbackEvent;Chain: Boolean); - destructor Destroy; override; - end; - - TRecNoStatus = (rnDbase, rnParadox, rnNotSupported); - - TPSQLSQLUpdateObject = class(TComponent) - protected - function GetDataSet: TPSQLDataSet; virtual; abstract; - procedure SetDataSet(ADataSet: TPSQLDataSet); virtual; abstract; - procedure Apply(UpdateKind: TUpdateKind); virtual; abstract; - function GetSQL(UpdateKind: TUpdateKind): TStrings; virtual; abstract; - property DataSet: TPSQLDataSet read GetDataSet write SetDataSet; - end; - - TKeyIndex = (kiLookup, kiRangeStart, kiRangeEnd, kiCurRangeStart, - kiCurRangeEnd, kiSave); - - PKeyBuffer = ^TKeyBuffer; - TKeyBuffer = packed record - Modified: Boolean; - Exclusive: Boolean; - FieldCount: Integer; - end; - - PRecInfo = ^TRecInfo; - TRecInfo = packed record - RecordNumber: Longint; - UpdateStatus: TUpdateStatus; - BookmarkFlag: TBookmarkFlag; - end; - - TBlobDataArray = array of TBlobData; - - TPSQLDataSet = class(TDataSet) - private - FAbout : TPSQLDACAbout; - FHandle: HDBICur; - FRecProps: RecProps; //Record properties - FExprFilter: HDBIFilter; //Filter expression - FFuncFilter: HDBIFilter; // filter function - FFilterBuffer: TRecordBuffer; // filter buffer - FIndexFieldMap: DBIKey; //Index field map - FExpIndex: Boolean; - FCaseInsIndex: Boolean; - FCachedUpdates: Boolean; - FInUpdateCallback: Boolean; - FCanModify: Boolean; - FCacheBlobs: Boolean; - FKeySize: integer; - FUpdateCBBuf: PDELAYUPDCbDesc; - FUpdateCallback: TPSQLBDECallBack; - FKeyBuffers: array[TKeyIndex] of PKeyBuffer; - FKeyBuffer: PKeyBuffer; - FRecNoStatus: TRecNoStatus; - FIndexFieldCount: Integer; - FRecordSize: integer; - FBookmarkOfs: integer; - FRecInfoOfs: integer; - FBlobCacheOfs: integer; - FRecBufSize: integer; - FBlockBufOfs: Integer; - FLastParentPos: Integer; - FBlockReadBuf: PAnsiDACChar; - {$IFNDEF FPC} - FBlockBufSize: Integer; - FBlockBufCount: Integer; - FBlockReadCount: Integer; - {$ENDIF} - FOldBuffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; - FParentDataSet: TPSQLDataSet; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FUpdateObject: TPSQLSQLUpdateObject; - {$IFNDEF FPC} - FOnUpdateError: TUpdateErrorEvent; - FOnUpdateRecord: TUpdateRecordEvent; - {$ENDIF} - FAutoRefresh: Boolean; - FDBFlags: TDBFlags; - FUpdateMode: TUpdateMode; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; - FAllowSequenced: boolean; - FSortFieldNames: string; - FOptions: TPSQLDatasetOptions; - {$IFDEF MOBILE} - FSetToRecBookm: TBookmark; - {$ENDIF} - - procedure ClearBlobCache(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); - function GetActiveRecBuf(var RecBuf: TRecordBuffer): Boolean; - function GetBlobData(Field: TField; Buffer: TRecordBuffer): TBlobData; - function GetOldRecord: PAnsiDACChar; - procedure InitBufferPointers(GetProps: Boolean); - function RecordFilter(RecBuf: Pointer; RecNo: Integer): Smallint; stdcall; - procedure SetBlobData(Field: TField; Buffer: TRecordBuffer; Value: TBlobData); - function GetDBHandle: DAChDBIDb; - procedure SetUpdateMode(const Value: TUpdateMode); - procedure SetAutoRefresh(const Value: Boolean); - procedure SetDatabase(Value : TPSQLDatabase); - function GetDatabase:TPSQLDatabase; - {$IFNDEF DELPHI_4}{$IFNDEF FPC} - procedure SetupAutoRefresh; - {$ENDIF}{$ENDIF} - function GetStmtHandle: HDBIStmt; - procedure SetSortFieldNames(const Value: string); - function GetSortFieldNames: string; - procedure ReadByteaOpt(Reader: TReader); //deal with old missing properties - procedure ReadOIDOpt(Reader: TReader); //deal with old missing properties - function GetStoreActive: boolean; - - protected - procedure DefineProperties(Filer: TFiler); override; - { IProviderSupport } - {$IFNDEF FPC} - procedure PSEndTransaction(Commit: Boolean); override; -{$IFDEF DELPHI_17} - function PSExecuteStatement(const ASQL: string; AParams: TParams): Integer; override; - function PSExecuteStatement(const ASQL: string; AParams: TParams; - var ResultSet: TDataSet): Integer; override; -{$ELSE} - function PSExecuteStatement(const ASQL: string; AParams: TParams; - ResultSet: Pointer = nil): Integer; override; -{$ENDIF DELPHI_17} - procedure PSGetAttributes(List: {$IFDEF DELPHI_19}TPacketAttributeList{$ELSE}TList{$ENDIF}); override; - function PSGetQuoteChar: string; override; - function PSInTransaction: Boolean; override; - function PSIsSQLBased: Boolean; override; - function PSIsSQLSupported: Boolean; override; - procedure PSStartTransaction; override; - procedure PSReset; override; - function PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; override; - {$ENDIF} - protected - function Engine : TPSQLEngine; Virtual; Abstract; - {$IFNDEF FPC} - procedure SetBlockReadSize(Value: Integer); override; - procedure BlockReadNext; override; - {$ENDIF} - procedure Notification(AComponent: TComponent; Operation: TOperation); override; - procedure ActivateFilters; - procedure AddFieldDesc(FieldDescs: TFLDDescList; var DescNo: Integer; - var FieldID: Integer; RequiredFields: TBits; FieldDefs: TFieldDefs); - procedure AllocCachedUpdateBuffers(Allocate: Boolean); - procedure AllocKeyBuffers; -{$IFDEF NEXTGEN} - function AllocRecBuf: TRecBuf; override; - procedure FreeRecBuf(var Buffer: TRecBuf); override; -{$ELSE} - function AllocRecordBuffer: TRecordBuffer; override; - procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override; -{$ENDIF NEXTGEN} - function CachedUpdateCallBack(CBInfo: Pointer): CBRType; - procedure CheckCachedUpdateMode; - procedure CheckSetKeyMode; - procedure ClearCalcFields(Buffer:{$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; - procedure CloseCursor; override; -{$IFDEF DELPHI_12} - procedure CreateFields; override; -{$ENDIF DELPHI_12} - procedure CloseBlob(Field: TField); override; - function CreateExprFilter(const Expr: string; - Options: TFilterOptions; Priority: Integer): HDBIFilter; - function CreateFuncFilter(FilterFunc: Pointer; - Priority: Integer): HDBIFilter; - function CreateHandle: HDBICur; virtual; - function CreateLookupFilter(Fields: TList{$IFDEF NEXTGEN}{$ENDIF}; const Values: Variant; - Options: TLocateOptions; Priority: Integer): HDBIFilter; - procedure DataEvent(Event: TDataEvent; Info: TDataEventInfo); override; - procedure DeactivateFilters; - procedure DestroyHandle; virtual; - function FindRecord(Restart, GoForward: Boolean): Boolean; override; - function ForceUpdateCallback: Boolean; - procedure FreeKeyBuffers; -{$IFNDEF NEXTGEN} - procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override; -{$ENDIF} -{$IFDEF DELPHI_17} - procedure GetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); override; -{$ENDIF DELPHI_17} - function GetBookmarkFlag(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}): TBookmarkFlag; override; - function GetRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override; - procedure InitRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; - -{$IFDEF NEXTGEN} - procedure InternalGotoBookmark(Bookmark: TBookmark); override; -{$ELSE} - procedure InternalGotoBookmark(Bookmark: Pointer); override; -{$ENDIF} - - procedure InternalInitRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; - procedure InternalSetToRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; - function GetCanModify: Boolean; override; -{$IFNDEF FPC} - function GetFieldFullName(Field: TField): string; override; -{$ENDIF} - function GetFieldClass(FieldType: TFieldType): TFieldClass; override; -{$IFDEF DELPHI_12} - function GetFieldClass(FieldDef: TFieldDef): TFieldClass; override; - function GetFieldDefsClass: TFieldDefsClass; override; - function GetLookupListClass(Field: TField): TLookupListClass; override; -{$ENDIF} - function GetIndexField(Index: Integer): TField; - function GetIndexFieldCount: Integer; - function GetIsIndexField(Field: TField): Boolean; override; - function GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer; - function GetKeyExclusive: Boolean; - function GetKeyFieldCount: Integer; - function GetRecordCount: Integer; override; - function GetRecNo: Integer; override; - function GetRecordSize: integer; reintroduce; virtual; - {$IFNDEF FPC} - function GetStateFieldValue(State: TDataSetState; Field: TField): Variant; override; - procedure GetObjectTypeNames(Fields: TFields); - {$ENDIF} - function GetUpdatesPending: Boolean; - {$IFNDEF FPC} - function GetUpdateRecordSet: TUpdateRecordTypes; - {$ENDIF} - function InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer; - procedure InternalAddRecord(Buffer: {$IFNDEF NEXTGEN}Pointer{$ELSE}TRecBuf{$ENDIF}; Append: Boolean); override; - procedure InternalCancel; override; - procedure InternalClose; override; - procedure InternalDelete; override; - procedure InternalEdit; override; - procedure InternalFirst; override; - - procedure InternalHandleException; override; - procedure InternalInitFieldDefs; override; - - procedure InternalInsert; override; - procedure InternalLast; override; - procedure InternalOpen; override; - procedure InternalPost; override; - procedure InternalRefresh; override; - function IsCursorOpen: Boolean; override; - function LocateRecord(const KeyFields: string; const KeyValues: Variant; - Options: TLocateOptions; SyncCursor: Boolean): Boolean; - function LocateFilteredRecord(const KeyFields: string; - const KeyValues: Variant; - Options: TLocateOptions; - SyncCursor: Boolean): Word; - function LocateNearestRecord(const KeyFields: string; const KeyValues: Variant; - Options: TLocateOptions; SyncCursor: Boolean): Word; - procedure PostKeyBuffer(Commit: Boolean); - procedure PrepareCursor; Virtual; - function ProcessUpdates(UpdCmd: DBIDelayedUpdCmd): Word; - function ResetCursorRange: Boolean; - {$IFNDEF NEXTGEN} - procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override; - {$ENDIF} - {$IFDEF DELPHI_17} - procedure SetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); override; - {$ENDIF DELPHI_17} - procedure SetBookmarkFlag(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Value: TBookmarkFlag); override; - procedure SetCachedUpdates(Value: Boolean); - function SetCursorRange: Boolean; - {$IFNDEF NEXTGEN} - procedure SetFieldData(Field: TField; Buffer: Pointer); override; - {$ENDIF} - {$IFDEF DELPHI_17} - procedure SetFieldData(Field: TField; Buffer: TValueBuffer); override; - {$ENDIF} - procedure SetFilterData(const Text: string; Options: TFilterOptions); - procedure SetFilterHandle(var Filter: HDBIFilter; Value: HDBIFilter); - procedure SetFiltered(Value: Boolean); override; - procedure SetFilterOptions(Value: TFilterOptions); override; - procedure SetFilterText(const Value: string); override; - procedure SetIndexField(Index: Integer; Value: TField); - procedure SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean); - procedure SetKeyExclusive(Value: Boolean); - procedure SetKeyFieldCount(Value: Integer); - procedure SetKeyFields(KeyIndex: TKeyIndex; const Values: array of const); - procedure SetLinkRanges(MasterFields: TList{$IFDEF DELPHI_17}{$ENDIF}); - {$IFNDEF FPC} - procedure SetStateFieldValue(State: TDataSetState; Field: TField; const Value: Variant); override; - {$ENDIF} - procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override; - {$IFNDEF FPC} - procedure SetOnUpdateError(UpdateEvent: TUpdateErrorEvent); - {$ENDIF} - procedure SetOptions(const Value: TPSQLDatasetOptions); virtual; - procedure SetRecNo(Value: Integer); override; - procedure SetupCallBack(Value: Boolean); - {$IFNDEF FPC} - procedure SetUpdateRecordSet(RecordTypes: TUpdateRecordTypes); - {$ENDIF} - procedure SetUpdateObject(Value: TPSQLSQLUpdateObject); - procedure SwitchToIndex(const IndexName, TagName: string); - function UpdateCallbackRequired: Boolean; - procedure Disconnect; Virtual; - procedure OpenCursor(InfoQuery: Boolean); override; - function SetDBFlag(Flag: Integer; Value: Boolean): Boolean; virtual; - function GetHandle: HDBICur; - property DBFlags: TDBFlags read FDBFlags; - property UpdateMode: TUpdateMode read FUpdateMode write SetUpdateMode default upWhereAll; - property StmtHandle: HDBIStmt read GetStmtHandle; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - function GetLastInsertID(const FieldNum: integer): integer; - function GetFieldTypeOID(const FieldNum: integer): cardinal; - procedure ApplyUpdates; - function BookmarkValid(Bookmark: TBookmark): Boolean; override; - procedure Cancel; override; - procedure CancelUpdates; - property CacheBlobs: Boolean read FCacheBlobs write FCacheBlobs default TRUE; - function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override; - procedure CommitUpdates; - procedure FetchAll; - procedure FlushBuffers; - function GetCurrentRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}): Boolean; override; - {$IFNDEF FPC} - function GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer; override; - {$ENDIF} - {$IFNDEF NEXTGEN} - function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override; - function GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; {$IFNDEF FPC}override;{$ENDIF} - {$ENDIF} - {$IFDEF DELPHI_17} - function GetFieldData(FieldNo: Integer; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; override; - function GetFieldData(Field: TField; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; override; - {$ENDIF} - procedure GetIndexInfo; - function Locate(const KeyFields: string; const KeyValues: Variant; - Options: TLocateOptions): Boolean; override; - function Lookup(const KeyFields: string; const KeyValues: Variant; - const ResultFields: string): Variant; override; - function IsSequenced: Boolean; override; - procedure Post; override; - procedure RevertRecord; - function UpdateStatus: TUpdateStatus; override; - function CheckOpen(Status: Word): Boolean; - procedure CloseDatabase(Database: TPSQLDatabase); - procedure GetDatabaseNames(List: TStrings); - property DBHandle: DAChDBIDb read GetDBHandle; - property Handle: HDBICur read GetHandle; - property ExpIndex: Boolean read FExpIndex; - property KeySize: integer read FKeySize; - property UpdateObject: TPSQLSQLUpdateObject read FUpdateObject write SetUpdateObject; - property UpdatesPending: Boolean read GetUpdatesPending; - {$IFNDEF FPC} - property UpdateRecordTypes: TUpdateRecordTypes read GetUpdateRecordSet write SetUpdateRecordSet; - {$ENDIF} - procedure PopulateFieldsOrigin(); - procedure SortBy(FieldNames : string; Compare : TPSQLDatasetSortCompare); overload; - procedure SortBy(FieldNames : string); overload; - property SortFieldNames : string read GetSortFieldNames write SetSortFieldNames; - property RecordSize: integer read GetRecordSize; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property AutoRefresh: Boolean read FAutoRefresh write SetAutoRefresh default FALSE; - property Database: TPSQLDatabase read GetDatabase write SetDatabase; - property CachedUpdates: Boolean read FCachedUpdates write SetCachedUpdates default False; - property AllowSequenced : Boolean read FAllowSequenced Write FAllowSequenced default False; //Added by Nicolas Ring - property Filter; - property Filtered; - property FilterOptions; - property OnFilterRecord; - property Active stored GetStoreActive; - property AutoCalcFields; - {$IFNDEF FPC} - property ObjectView default FALSE; - {$ENDIF} - property Options: TPSQLDatasetOptions read FOptions write SetOptions; - property BeforeOpen; - property AfterOpen; - property BeforeClose; - property AfterClose; - property BeforeInsert; - property AfterInsert; - property BeforeEdit; - property AfterEdit; - property BeforePost; - property AfterPost; - property BeforeCancel; - property AfterCancel; - property BeforeDelete; - property AfterDelete; - property BeforeScroll; - property AfterScroll; - property BeforeRefresh; - property AfterRefresh; - property OnCalcFields; - property OnDeleteError; - property OnEditError; - property OnNewRecord; - property OnPostError; - {$IFNDEF FPC} - property OnUpdateError: TUpdateErrorEvent read FOnUpdateError write SetOnUpdateError; - property OnUpdateRecord: TUpdateRecordEvent read FOnUpdateRecord write FOnUpdateRecord; - {$ENDIF} - end; - -////////////////////////////////////////////////////////// -//Class : TPSQLTable -//Description : TPSQLTable class -////////////////////////////////////////////////////////// - TTableType = (ttDefault, ttParadox, ttDBase, ttFoxPro, ttASCII); - TIndexName = type string; - - TValCheckList = array of VCHKDesc; - - TPSQLTable = Class(TPSQLDataSet) - Private - FStoreDefs: Boolean; - FIndexDefs: TIndexDefs; - FMasterLink: TMasterDataLink; - FDefaultIndex: Boolean; - FExclusive: Boolean; - FReadOnly: Boolean; - FFieldsIndex: Boolean; - FTableName: TFileName; - FIndexName: TIndexName; - FTableLevel: Integer; - FNativeTableName: DBITBLNAME; - FLimit : Integer; - FOffset: Integer; - FShowSystemTables: boolean; - FTableID: cardinal; - FComment: string; - FOwner: string; - FTablespace: string; - procedure SetLimit(const Value : Integer); - function GetLimit: integer; - procedure CheckMasterRange; - procedure DecodeIndexDesc(const IndexDesc: IDXDesc; - var Source, Name, FieldExpression, DescFields: string; - var Options: TIndexOptions); - function FieldDefsStored: Boolean; - function GetExists: Boolean; - function GetIndexFieldNames: string; - function GetIndexName: string; - procedure GetIndexParams(const IndexName: string; FieldsIndex: Boolean; - var IndexedName, IndexTag: string); - function GetMasterFields: string; - function GetTableLevel: Integer; - function IndexDefsStored: Boolean; - procedure MasterChanged(Sender: TObject); - procedure MasterDisabled(Sender: TObject); - procedure SetDataSource(Value: TDataSource); - procedure SetExclusive(Value: Boolean); - procedure SetIndexDefs(Value: TIndexDefs); - procedure SetIndex(const Value: string; FieldsIndex: Boolean); - procedure SetIndexFieldNames(const Value: string); - procedure SetIndexName(const Value: string); - procedure SetMasterFields(const Value: string); - procedure SetReadOnly(Value: Boolean); - procedure SetTableName(const Value: TFileName); - function GetTableName: TFileName; - procedure UpdateRange; - function GetBatchModify: Boolean; - procedure SetBatchModify(const Value : Boolean); - procedure SetShowSystemTables(const Value: boolean); - function GetOffset: Integer; - procedure SetOffset(const Value: Integer); - procedure SetDummyInt(const Value: cardinal); - procedure SetDummyStr(const Value: string); - function GetTableSpace: string; - Protected - { IProviderSupport } - {$IFNDEF FPC} - function PSGetDefaultOrder: TIndexDef; override; - function PSGetKeyFields: string; override; - function PSGetTableName: string; override; - function PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs; override; - procedure PSSetCommandText(const CommandText: string); override; - procedure PSSetParams(AParams: TParams); override; - {$ENDIF} - function CreateHandle: HDBICur; override; - procedure DataEvent(Event: TDataEvent; Info: TDataEventInfo); override; - {$IFNDEF FPC} - procedure DefChanged(Sender: TObject); override; - {$ENDIF} - procedure DestroyHandle; override; - procedure DoOnNewRecord; override; - procedure EncodeFieldDesc(var FieldDesc: FLDDesc; - const Name: string; DataType: TFieldType; Size, Precision: Integer); - procedure EncodeIndexDesc(var IndexDesc: IDXDesc; - const Name, FieldExpression: string; Options: TIndexOptions; - const DescFields: string = ''); - function GetCanModify: Boolean; override; - function GetDataSource: TDataSource; override; - function GetHandle(const IndexName, IndexTag: string): HDBICur; - function GetLanguageDriverName: string; - procedure InitFieldDefs; override; - function GetFileName: string; - function GetTableType: TTableType; - function NativeTableName: PAnsiDACChar; - procedure PrepareCursor; override; - procedure UpdateIndexDefs; override; - procedure SetOptions(const Value: TPSQLDatasetOptions); override; - property MasterLink: TMasterDataLink read FMasterLink; - Public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - function Engine : TPSQLEngine; override; - function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override; - function IsSequenced: Boolean; override; - procedure AddIndex(const Name, Fields: string; Options: TIndexOptions; const DescFields: string = ''); - procedure ApplyRange; - procedure CancelRange; - procedure CreateTable; - procedure DeleteIndex(const Name: string); - procedure EditKey; - procedure EditRangeEnd; - procedure EditRangeStart; - procedure EmptyTable; - function FindKey(const KeyValues: array of const): Boolean; - procedure FindNearest(const KeyValues: array of const); - {$IFNDEF FPC} - procedure GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF DELPHI_17}{$ENDIF}); override; - {$ENDIF} - procedure GetIndexNames(List: TStrings); - procedure GotoCurrent(Table: TPSQLTable); - function GotoKey: Boolean; - procedure GotoNearest; - Procedure LockTable(LockType: TPSQLLockType; NoWait: boolean); - procedure SetKey; - procedure SetRange(const StartValues, EndValues: array of const); - procedure SetRangeEnd; - procedure SetRangeStart; - property Exists: Boolean read GetExists; - property IndexFieldCount: Integer read GetIndexFieldCount; - property IndexFields[Index: Integer]: TField read GetIndexField write SetIndexField; - property KeyExclusive: Boolean read GetKeyExclusive write SetKeyExclusive; - property KeyFieldCount: Integer read GetKeyFieldCount write SetKeyFieldCount; - property TableLevel: Integer read GetTableLevel write FTableLevel; - property BatchModify : Boolean read GetBatchModify write SetBatchModify default False; - published - property DefaultIndex: Boolean read FDefaultIndex write FDefaultIndex default TRUE; - property Exclusive: Boolean read FExclusive write SetExclusive default FALSE; - property FieldDefs stored FieldDefsStored; - property IndexDefs: TIndexDefs read FIndexDefs write SetIndexDefs stored IndexDefsStored; - property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames; - property IndexName: string read GetIndexName write SetIndexName; - property MasterFields: string read GetMasterFields write SetMasterFields; - property MasterSource: TDataSource read GetDataSource write SetDataSource; - property ReadOnly: Boolean read FReadOnly write SetReadOnly default FALSE; - property StoreDefs: Boolean read FStoreDefs write FStoreDefs default FALSE; - property ShowSystemTables: boolean read FShowSystemTables write SetShowSystemTables default False; - property TableName: TFileName read GetTableName write SetTableName; - property UpdateMode; - property UpdateObject; - property Limit : Integer read GetLimit write SetLimit default 0; - property Offset : Integer read GetOffset write SetOffset default 0; - property Owner: string read FOwner write SetDummyStr stored False; - property Tablespace: string read GetTableSpace write SetDummyStr stored False; - property TableID: cardinal read FTableID write SetDummyInt stored False; - property Comment: string read FComment write SetDummyStr stored False; - property SortFieldNames; - end; - - -////////////////////////////////////////////////////////// -//Class : TPSQLQuery -//Description : Component TPSQLQuery -////////////////////////////////////////////////////////// - TPSQLQuery = Class(TPSQLDataSet) - Private - FSQL: TStrings; - FPrepared: Boolean; - FParams: TPSQLParams; - FText: string; - FDataLink: TDataLink; - FLocal: Boolean; - FRowsAffected: Integer; - FUniDirectional: Boolean; - FRequestLive: Boolean; - FSQLBinary: PChar; - FParamCheck: Boolean; - FExecSQL: Boolean; - FCheckRowsAffected: Boolean; - FBeforeExecSQL: TDataSetNotifyEvent; - function CreateCursor(GenHandle: Boolean): HDBICur; - function GetQueryCursor(GenHandle: Boolean): HDBICur; - function GetRowsAffected: Integer; - procedure PrepareSQL(Value: PChar); - procedure QueryChanged(Sender: TObject); - procedure ReadBinaryData(Stream: TStream); - procedure ReadParamData(Reader: TReader); - procedure RefreshParams; - procedure SetDataSource(Value: TDataSource); - procedure SetQuery(Value: TStrings); - function GetQuery:TStrings; - procedure SetParamsList(Value: TPSQLParams); - procedure SetParamsFromCursor; - procedure SetPrepared(Value: Boolean); - procedure SetPrepare(Value: Boolean); - procedure WriteBinaryData(Stream: TStream); - procedure WriteParamData(Writer: TWriter); - procedure SetRequestLive(const Value : Boolean); - function GetRequestLive : Boolean; - protected - { IProviderSupport } - {$IFNDEF FPC} - procedure PSExecute; override; - function PSGetDefaultOrder: TIndexDef; override; - function PSGetParams: TParams; override; - function PSGetTableName: string; override; - procedure PSSetCommandText(const CommandText: string); override; - procedure PSSetParams(AParams: TParams); override; - {$ENDIF} - function CreateHandle: HDBICur; override; - procedure DefineProperties(Filer: TFiler); override; - procedure Disconnect; override; - function GetDataSource: TDataSource; override; - function GetParamsCount: integer; - function SetDBFlag(Flag: Integer; Value: Boolean): Boolean; override; - procedure SetOptions(const Value: TPSQLDatasetOptions); override; - procedure GetStatementHandle(SQLText: PChar); virtual; - property DataLink: TDataLink read FDataLink; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - function Engine : TPSQLEngine; override; - function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override; - function IsSequenced: Boolean; override; - procedure ExecSQL; - {$IFNDEF FPC} - procedure GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF NEXTGEN}{$ENDIF}); override; - {$ENDIF} - function ParamByName(const Value: string): TPSQLParam; - procedure Prepare; - procedure UnPrepare; - property Prepared: Boolean read FPrepared write SetPrepare; - property ParamCount: integer read GetParamsCount; - property Local: Boolean read FLocal; - property Text: string read FText; - property RowsAffected: Integer read GetRowsAffected; - property SQLBinary: PChar read FSQLBinary write FSQLBinary; - published - property BeforeExecSQL: TDataSetNotifyEvent read FBeforeExecSQL write FBeforeExecSQL; - property DataSource: TDataSource read GetDataSource write SetDataSource; - property ParamCheck: Boolean read FParamCheck write FParamCheck default TRUE; - property RequestLive: Boolean read GetRequestLive write SetRequestLive default FALSE; - property SQL: TStrings read GetQuery write SetQuery; - property Params: TPSQLParams read FParams write SetParamsList stored FALSE; - property UniDirectional: Boolean read FUniDirectional write FUniDirectional default FALSE; - property UpdateMode; - property UpdateObject; - property SortFieldNames; - end; - - - TRecordChangeCompleteEvent = procedure(DataSet: TPSQLDataSet; const Reason: TUpdateKind) of object; - - { TPSQLUpdateSQL } - TPSQLUpdateSQL = Class(TPSQLSQLUpdateObject) - private - FAbout : TPSQLDACAbout; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDataSet: TPSQLDataSet; - FQueries: array[TUpdateKind] of TPSQLQuery; - FSQLText: array[TUpdateKind] of TStrings; - FRecordChangeCompleteEvent: TRecordChangeCompleteEvent; - function GetQuery(UpdateKind: TUpdateKind): TPSQLQuery; - function GetSQLIndex(Index: Integer): TStrings; - procedure SetSQL(UpdateKind: TUpdateKind; Value: TStrings); - procedure SetSQLIndex(Index: Integer; Value: TStrings); - protected - function GetSQL(UpdateKind: TUpdateKind): TStrings; override; - function GetQueryClass : TPSQLQueryClass; - function GetDataSet: TPSQLDataSet; override; - procedure SetDataSet(ADataSet: TPSQLDataSet); override; - procedure SQLChanged(Sender: TObject); - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - procedure Apply(UpdateKind: TUpdateKind); override; - procedure ExecSQL(UpdateKind: TUpdateKind); - procedure SetParams(UpdateKind: TUpdateKind); - property DataSet; - property Query[UpdateKind: TUpdateKind]: TPSQLQuery read GetQuery; - property SQL[UpdateKind: TUpdateKind]: TStrings read GetSQL write SetSQL; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property ModifySQL: TStrings index 0 read GetSQLIndex write SetSQLIndex; - property InsertSQL: TStrings index 1 read GetSQLIndex write SetSQLIndex; - property DeleteSQL: TStrings index 2 read GetSQLIndex write SetSQLIndex; - property OnRecordChangeComplete: TRecordChangeCompleteEvent read FRecordChangeCompleteEvent write FRecordChangeCompleteEvent; - end; - - { TPSQLBlobStream } - TPSQLBlobStream = class(TStream) - private - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FField: TBlobField; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDataSet: TPSQLDataSet; - FBuffer: TRecordBuffer; - FMode: TBlobStreamMode; - FFieldNo: Integer; - FOpened: Boolean; - FModified: Boolean; - FPosition: Longint; - FBlobData: TBlobData; - FCached: Boolean; - FCacheSize: Longint; - function GetBlobSize: integer; - public - constructor Create(Field: TBlobField; Mode: TBlobStreamMode); - destructor Destroy; override; - function Engine : TPSQLEngine; - function PositionDataset: Boolean; - {$IFDEF DELPHI_17} - function Read(Buffer: TBytes; Offset, Count: Longint): Longint; override; - {$ENDIF DELPHI_17} - function Read(var Buffer; Count: Longint): Longint; override; - function Write(const Buffer; Count: Longint): Longint; override; - function Seek(Offset: Longint; Origin: Word): Longint; override; - procedure Truncate; - end; - - - TParamBindMode = (pbByName, pbByNumber); - - TSPParamDescList = array of SPParamDesc; - - TPSQLStoredProc = class(TPSQLDataSet) - private - FParams: TPSQLParams; - FNeedRefreshParams : boolean; - FOverload: cardinal; - FProcName: string; - FBindMode: TParamBindMode; - function GetParamsCount: integer; - procedure SetOverload(const Value: cardinal); - procedure SetProcName(const Value: string); - protected - { IProviderSupport } - {$IFNDEF FPC} - procedure PSExecute; override; - function PSGetTableName: string; override; - function PSGetParams: TParams; override; - procedure PSSetCommandText(const CommandText: string); override; - procedure PSSetParams(AParams: TParams); override; - {$ENDIF} - function CreateHandle: HDBICur;override; - function CreateCursor(IsExecProc : boolean): HDBICur; - procedure DataEvent(Event: TDataEvent; Info: TDataEventInfo); override; - procedure CloseCursor; override; - procedure SetProcedureName(const Value: string); - function GetParamsList: TPSQLParams; - procedure SetParamsList(const Value: TPSQLParams); - function GetCanModify: Boolean; override; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override; - function Engine : TPSQLEngine; override; - function DescriptionsAvailable: Boolean; - function ParamByName(const Value: string): TPSQLParam; - - procedure ExecProc; - procedure RefreshParams; - procedure SetNeedRefreshParams; - - property ParamsCount : integer read GetParamsCount; - published - property StoredProcName: string read FProcName write SetProcName; - property Overload: cardinal read FOverload write SetOverload default 0; - property Params: TPSQLParams read GetParamsList write SetParamsList; - property ParamBindMode: TParamBindMode read FBindMode write FBindMode default pbByName; - end; - -procedure Check(Engine : TPSQLEngine; Status: Word); -procedure NoticeProcessor(arg: Pointer; mes: PAnsiDACChar); cdecl; - -var - DBList : TList{$IFDEF NEXTGEN}{$ENDIF}; - -implementation - -uses - {$IFDEF DELPHI_10}DBClient, {$ENDIF} - PSQLDirectQuery, Math, PSQLFields, PSQLNotify - {$IFDEF NEXTGEN}, System.Character{$ENDIF}; - -//NoticeProcessor callback function -procedure NoticeProcessor(arg: Pointer; mes: PAnsiDACChar); -var s:string; -begin - if not Assigned(TPSQLDatabase(Arg).FOnNotice) then Exit; - if TPSQLDatabase(Arg).IsUnicodeUsed then - S := UTF8ToString(Mes) - else - S := string(Mes); - TPSQLDatabase(Arg).FOnNotice(TPSQLDatabase(Arg), S); -end; - -{ TPSQLQueryDataLink } -type - - TPSQLQueryDataLink = Class(TDetailDataLink) - private - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FQuery: TPSQLQuery; - protected - procedure ActiveChanged; override; - procedure RecordChanged(Field: TField); override; - function GetDetailDataSet: TDataSet; override; - procedure CheckBrowseMode; override; - public - constructor Create(AQuery: TPSQLQuery); - end; - -procedure TDbiError(Engine : TPSQLEngine; ErrorCode: Word); -begin - Raise EPSQLDatabaseError.Create(Engine, ErrorCode); -end; - -procedure Check(Engine : TPSQLEngine; Status: Word); -begin - if Status <> 0 then TDbiError(Engine, Status); -end; - -function GetIntProp(Engine : TPSQLEngine; const Handle: Pointer; PropName: Integer): Integer; -Var - Length : integer; - Value : Integer; -begin - Value := 0; - if (Engine.GetEngProp(HDBIObj(Handle), PropName, @Value, SizeOf(Value), Length) = DBIERR_NONE) then - Result := Value else - Result := 0; -end; - -function SetBoolProp(Engine : TPSQLEngine; const Handle: Pointer; PropName: Integer; Value: Boolean) : Boolean; -begin - Result := Engine.SetEngProp(HDBIObj(Handle), PropName, Abs(Integer(Value))) = DBIERR_NONE; -end; - -{ EPSQLDatabaseError } -constructor EPSQLDatabaseError.Create(Engine : TPSQLEngine; ErrorCode : Word); - - function GetErrorString: string; - var - Msg1 : string; - Msg2 : string; - Err : Integer; - begin - Msg1 := Engine.MessageStatus; - Err := Engine.Status; - if (Msg1 <> '') and (Err > 0) then - Msg1 := Format('PostgreSQL Error Code: (%s)'#13#10'%s', [FErrorsqlstate, Msg1]) - else - begin - Msg2 := GetBDEErrorMessage(ErrorCode); - Msg1 := Format('DBI Error Code: (%d)'#13#10'%s '#13#10'%s', [ErrorCode, Msg1, Msg2]); - end; - Result := Msg1 - end; - -var - NC: TNativeConnect; -begin - inherited Create(GetErrorString()); - FErrorCode := ErrorCode; - if Assigned(Engine.Database) then - begin - NC := TNativeConnect(Engine.Database); - FErrorPos := NC.FErrorPos; - FErrorContext := NC.FErrorContext; - FErrorseverity := NC.FErrorSeverity; - FErrorsqlstate := NC.FErrorsqlstate; - FErrorprimary := NC.FErrorprimary; - FErrordetail := NC.FErrordetail; - FErrorhint := NC.FErrorhint; - FErrorinternalpos := NC.FErrorinternalpos; - FErrorinternalquery := NC.FErrorinternalquery; - FErrorsourcefile := NC.FErrorsourcefile; - FErrorsourceline := NC.FErrorsourceline; - FErrorsourcefunc := NC.FErrorsourcefunc; - FErrorSchemaName := NC.FErrorSchemaName; - FErrorTableName := NC.FErrorTableName; - FErrorColumnName := NC.FErrorColumnName; - FErrorDataTypeName := NC.FErrorDataTypeName; - FErrorConstraintName := NC.FErrorConstraintName; - end; - if Message = EmptyStr then - Message := Format('PSQLDAC Interface Error: (%d)',[ErrorCode]); -end; - -destructor EPSQLDatabaseError.Destroy; -begin - Inherited Destroy; -end; - - -{ TPSQLDatabase } -procedure TPSQLDatabase.InitEngine; -begin - try - if FEngine = nil then FEngine := TPSQLEngine.Create(nil, nil); - except - raise EDatabaseError.Create('The Engine is not initialized'); - end; -end; - -procedure TPSQLDatabase.AddDatabase(Value : TPSQLDatabase); -begin - DBList.Add(Value); -end; - -procedure TPSQLDatabase.RemoveDatabase(Value : TPSQLDatabase); -begin - while DataSetCount <> 0 do - TPSQLDataSet(DataSets[DataSetCount - 1]).FDatabase := nil; - - if Assigned(DBList) then - DBList.Remove(Value); - - while FDirectQueryList.Count > 0 do - TPSQLDirectQuery(FDirectQueryList[FDirectQueryList.Count - 1]).Database := nil; -end; - -procedure TPSQLDatabase.WriteState(Writer: TWriter); -var OldPwd: string; -begin - if not (ddoStorePassword in FDesignOptions) then - begin - OldPwd := GetUserPassword(); - SetUserPassword(''); - end; - - inherited; - - if not (ddoStorePassword in FDesignOptions) then - SetUserPassword(OldPwd); -end; - -constructor TPSQLDatabase.Create(AOwner : TComponent); -begin - Inherited Create(AOwner); - FParams := TStringList.Create; - TStringList(FParams).OnChanging := ParamsChanging; - FKeepConnection := TRUE; - FOEMConvert := False; - SetConnectionTimeout(15); - SetServerPort(PSQL_PORT); - SetSSLMode(sslPrefer); - FTransIsolation := tiReadCommitted; - AddDatabase(Self); - FNotifyList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - FDirectQueryList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - FCheckIfActiveOnParamChange := True; //SSH Tunneling stuff - SetConnectionTimeout(15); - FDatabaseID := InvalidOid; - FDesignOptions := [ddoStoreConnected, ddoStorePassword]; - FErrorVerbosity := evDEFAULT; -end; - -destructor TPSQLDatabase.Destroy; -begin - Destroying; - Close; - RemoveDatabase(Self); - FEngine.Free; - FNotifyList.Free; - FDirectQueryList.Free; - FParams.Free; - FStmtList.Free; - inherited Destroy; -end; - - -procedure TPSQLDatabase.ApplyUpdates(const DataSets: array of TPSQLDataSet); -var - I : Integer; - DS : TPSQLDataSet; -begin - StartTransaction; - try - for I := 0 to High(DataSets) do - begin - DS := DataSets[I]; - if (DS.Database <> Self) then - DatabaseError(Format(SUpdateWrongDB, [DS.Name, Name])); - DataSets[I].ApplyUpdates; - end; - Commit; - except - Rollback; - raise; - end; - for I := 0 to High(DataSets) do DataSets[I].CommitUpdates; -end; - -procedure TPSQLDatabase.AssignTo(Dest: TPersistent); -var DestDB: TPSQLDatabase; -begin - if not (Dest is TPSQLDatabase) then - inherited AssignTo(Dest) - else - begin - DestDB := TPSQLDatabase(Dest); - DestDB.Params.Assign(Self.Params); - end; -end; - -type - PStmtInfo = ^TStmtInfo; - TStmtInfo = packed record - HashCode: Integer; - StmtHandle: HDBIStmt; - SQLText: string; - end; - -procedure TPSQLDatabase.ClearStatements; -var - i: Integer; -begin - if Assigned(FStmtList) then - begin - for i := 0 to FStmtList.Count - 1 do - begin - Engine.QFree(PStmtInfo(FStmtList[i]).StmtHandle); - Dispose(PStmtInfo(FStmtList[i])); - end; - FStmtList.Clear; - end; -end; - -function TPSQLDatabase.Execute(const SQL: string; Params: TParams = nil; - Cache: Boolean = FALSE; Cursor: phDBICur = nil): Integer; - - function GetStmtInfo(SQL: PChar): PStmtInfo; - - function GetHashCode(Str: PChar): Integer; - var - Off, Len, Skip, I: Integer; - begin - Result := 0; - Off := 1; - Len := StrLen(Str); - if Len < 16 then - for I := (Len - 1) downto 0 do - begin - Result := (Result * 37) + Ord(Str[Off]); - Inc(Off); - end - else - begin - { Only sample some characters } - Skip := Len div 8; - I := Len - 1; - while I >= 0 do - begin - Result := (Result * 39) + Ord(Str[Off]); - Dec(I, Skip); - Inc(Off, Skip); - end; - end; - end; - - var - HashCode, i: Integer; - Info: PStmtInfo; - begin - if not Assigned(FStmtList) then - FStmtList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - Result := nil; - HashCode := GetHashCode(SQL); - for i := 0 to FStmtList.Count - 1 do - begin - Info := PStmtInfo(FStmtList[i]); - if (Info.HashCode = HashCode) and - AnsiSameText(PChar(Info.SQLText), SQL) then - begin - Result := Info; - break; - end; - end; - if not Assigned(Result) then - begin - New(Result); - FStmtList.Add(Result); - FillChar(Result^, SizeOf(Result^), 0); - Result.HashCode := HashCode; - end; - end; - - function GetStatementHandle: HDBIStmt; - var - Info: PStmtInfo; - Status: Word; - begin - Info := nil; - Result := nil; - if Cache then - begin - Info := GetStmtInfo(PChar(SQL)); - Result := Info.StmtHandle; - end; - if not Assigned(Result) then - begin - Check(Engine, Engine.QAlloc(Handle, Result)); - if Cursor <> nil then - Check(Engine, Engine.SetEngProp(hDbiObj(Result), stmtLIVENESS, Ord(wantCanned))); - SetBoolProp(Engine, Result, stmtUNIDIRECTIONAL, TRUE); - while TRUE do - begin - Status := Engine.QPrepare(Result, SQL); - case Status of - DBIERR_NONE: break; - DBIERR_NOTSUFFTABLERIGHTS: TDbiError(Engine, Status); - end; - end; - if Assigned(Info) then - begin - Info.SQLText := SQL; - Info.StmtHandle := Result; - end; - end; - end; - -var - StmtHandle : HDBIStmt; - Len : integer; -begin - StmtHandle := nil; - Result := 0; - Open; - if Assigned(Params) and (Params.Count > 0) then - begin - StmtHandle := GetStatementHandle; - try - Check(Engine, Engine.QuerySetParams(StmtHandle, Params, SQL)); - Check(Engine, Engine.QExec(StmtHandle, Cursor, Result)); - finally - if not Cache then Engine.QFree(StmtHandle); - end; - end - else - Check(Engine, Engine.QExecDirect(Handle, SQL, Cursor, Result)); - if Result = 0 then - if (Cursor = nil) and (Engine.GetEngProp(hDBIObj(StmtHandle), stmtROWCOUNT,@Result, SizeOf(Result), Len) <> 0) then - Result := 0; -end; - -procedure TPSQLDatabase.CheckActive; -begin - if FHandle = nil then DatabaseError(SDatabaseClosed); -end; - -procedure TPSQLDatabase.CheckInactive; -begin - if FHandle <> nil then - if csDesigning in ComponentState then - Close else - DatabaseError(SDatabaseOpen, Self); -end; - -procedure TPSQLDatabase.CloseDatabaseHandle; -begin - Engine.CloseDatabase(FHandle); -end; - -procedure TPSQLDatabase.CloseDatabase(Database: TPSQLDatabase); -begin - with Database do - begin - if FRefCount <> 0 then Dec(FRefCount); - if (FRefCount = 0) and not KeepConnection then Close; - end; -end; - -procedure TPSQLDatabase.DefineProperties(Filer: TFiler); -begin - inherited; - Filer.DefineProperty('UseSSL', SetUseSSL, nil, False); //missing -end; - -function TPSQLDatabase.GetSSLOption(Index: integer): string; -begin - Result := FParams.Values[SSLOpts[Index]]; -end; - -procedure TPSQLDatabase.SetSSLOption(Index: integer; Value: string); -begin - FParams.Values[SSLOpts[Index]] := Value; -end; - -procedure TPSQLDatabase.DoDisconnect; -begin - if FHandle <> nil then - begin - ClearStatements; - CloseDataSets; - if not FAcquiredHandle then - CloseDatabaseHandle else - FAcquiredHandle := FALSE; - FHandle := nil; - FRefCount := 0; - FDatabaseID := 0; - FIsTemplate := False; - FTablespace := ''; - FComment := ''; - FServerVersion := ''; - FOwner := ''; - end; -end; - -procedure TPSQLDatabase.CloseDataSets; -begin - while DataSetCount <> 0 do - TPSQLDataSet(DataSets[DataSetCount - 1]).Disconnect; -end; - -procedure TPSQLDatabase.Commit; -begin - CheckActive; - EndTransaction(xendCOMMIT); -end; - -procedure TPSQLDatabase.Rollback; -begin - CheckActive; - EndTransaction(xendABORT); -end; - -procedure TPSQLDatabase.StartTransaction; -var - TransHandle: HDBIXAct; -begin - CheckActive; - Check(Engine, Engine.BeginTran(FHandle, EXILType(FTransIsolation),TransHandle)); -end; - -procedure TPSQLDatabase.EndTransaction(TransEnd : EXEnd); -begin - Check(Engine, Engine.EndTran(FHandle, nil, TransEnd)); -end; - -procedure TPSQLDatabase.Savepoint(const Name: string); -begin - CheckActive(); - Execute('SAVEPOINT ' + Name); -end; - -procedure TPSQLDatabase.ReleaseSavepoint(const Name: string); -begin - CheckActive(); - Execute('RELEASE SAVEPOINT ' + Name); -end; - -procedure TPSQLDatabase.ReloadGUC; -begin - if Assigned(FHandle) then - TNativeConnect(FHandle).ReloadGUC(); -end; - -procedure TPSQLDatabase.RollbackToSavepoint(const Name: string); -begin - CheckActive(); - Execute('ROLLBACK TO SAVEPOINT ' + Name); -end; - -function TPSQLDatabase.GetConnected: Boolean; -begin - Result := FHandle <> nil; -end; - -function TPSQLDatabase.GetConnectionTimeout: cardinal; -begin - Result := StrToUIntDef(FParams.Values['connect_timeout'], 15); -end; - -function TPSQLDatabase.GetStoreConnected: Boolean; -begin - Result := Connected and - (ddoStoreConnected in FDesignOptions); -end; - -function TPSQLDatabase.GetDataSet(Index : Integer) : TPSQLDataSet; -begin - Result := inherited GetDataSet(Index) as TPSQLDataSet; -end; - -procedure TPSQLDatabase.SetDatabaseFlags; -var - Length: integer; - Buffer: DBINAME; -begin - Check(Engine, Engine.GetEngProp(HDBIOBJ(FHandle), dbDATABASETYPE, @Buffer, SizeOf(Buffer), Length)); - FPseudoIndexes := FALSE; -end; - -function TPSQLDatabase.GetInTransaction: Boolean; -var - TranInfo : XInfo; -begin - Result := (Handle <> nil) and - (Engine.GetTranInfo(Handle, nil, @TranInfo) = DBIERR_NONE) and - ( (TranInfo.exState = xsActive)); -end; - -function TPSQLDatabase.GetServerPort: Cardinal; -begin - Result := StrToUIntDef(FParams.Values['port'], PSQL_PORT); -end; - -function TPSQLDatabase.GetServerVersionAsInt: integer; -begin - if Assigned(Handle) then - Result := TNativeConnect(Handle).GetserverVersionAsInt - else - Result := 0; -end; - -function TPSQLDatabase.GetTransactionStatus: TTransactionStatusType; -begin - if Handle <> nil then - Engine.GetTranStatus(Handle,Result) - else - Result := trstUnknown; -end; - -procedure TPSQLDatabase.Loaded; - - procedure ChangeOldParameter(const OldName, NewName: string); - var I: integer; - V: string; - begin - I := FParams.IndexOfName(OldName); - if I = -1 then Exit; - V := Copy(FParams[I], Length(OldName) + 2, MaxInt); - FParams.Delete(I); - FParams.Values[NewName] := V; - end; - -begin - inherited Loaded; - if not StreamedConnected then InitEngine; - TStringList(FParams).OnChanging := nil; - try - ChangeOldParameter('UID', 'user'); - ChangeOldParameter('PWD', 'password'); - ChangeOldParameter('DatabaseName', 'dbname'); - ChangeOldParameter('ConnectionTimeout', 'connect_timeout'); - ChangeOldParameter('Port', 'port'); - ChangeOldParameter('SSLMode', 'sslmode'); - ChangeOldParameter('Host', 'host'); - if IsValidIP(FParams.Values['host']) then - begin - FParams.Values['hostaddr'] := FParams.Values['host']; - FParams.Values['host'] := ''; - end; - finally - TStringList(FParams).OnChanging := ParamsChanging; - end; -end; - -procedure TPSQLDatabase.Notification(AComponent : TComponent; Operation : TOperation); -begin - inherited Notification(AComponent, Operation); -end; - -procedure TPSQLDatabase.Login(LoginParams: TStrings); -begin - if Assigned(FOnLogin) then - FOnLogin(Self, LoginParams) - else - DatabaseErrorFmt(SLoginPrompt, [DatabaseName]); -end; - -procedure TPSQLDatabase.DoConnect; -const - OpenModes: array[Boolean] of DbiOpenMode = (dbiReadWrite, dbiReadOnly); - ShareModes: array[Boolean] of DbiShareMode = (dbiOpenShared, dbiOpenExcl); -var - s: String; -begin - if FHandle = nil then - begin - InitEngine; - if LoginPrompt then Login(FParams); - OEMConv := FOEMConvert; - s:= FParams.Text; - Check(Engine, Engine.OpenDatabase(FParams, FUseSingleLineConnInfo, FHandle)); - Check(Engine, Engine.GetServerVersion(FHandle, FServerVersion)); - Check(Engine, Engine.SetCharacterSet(FHandle, FCharSet)); - Check(Engine, Engine.SetCommandTimeout(FHandle, FCommandTimeout)); - if FErrorVerbosity <> evDEFAULT then - Check(Engine, Engine.SetErrorVerbosity(FHandle, FErrorVerbosity)); - if Assigned(FHandle) then - PQSetNoticeProcessor(TNativeConnect(FHandle).Handle, NoticeProcessor, Self); - SetBoolProp(Engine, FHandle, dbUSESCHEMAFILE, TRUE); - SetBoolProp(Engine, FHandle, dbPARAMFMTQMARK, TRUE); - SetBoolProp(Engine, FHandle, dbCOMPRESSARRAYFLDDESC, TRUE); - SetDatabaseFlags; - end; -end; - -procedure TPSQLDatabase.ParamsChanging(Sender: TObject); -begin - if FCheckIfActiveOnParamChange then CheckInactive; //SSH tunneling -end; - -function TPSQLDatabase.Ping(ConnectionParams: TStrings): TPingStatus; -begin - Check(Engine, Engine.Ping(ConnectionParams, Result)); -end; - -function TPSQLDatabase.Ping: TPingStatus; -begin - Check(Engine, Engine.Ping(FParams, Result)); -end; - -procedure TPSQLDatabase.SetDatabaseName(const Value : string); -begin - if not (csReading in ComponentState) then - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FParams.Values['dbname'] := Value; -end; - -procedure TPSQLDatabase.SetServerPort(const Value : Cardinal); -begin - if not (csReading in ComponentState) then - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FParams.Values['port'] := UIntToStr(Value); -end; - -procedure TPSQLDatabase.SetConnectionTimeout(const Value : Cardinal); -begin - if not (csReading in ComponentState) then - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FParams.Values['connect_timeout'] := IntToStr(Value); -end; - -procedure TPSQLDatabase.SetCommandTimeout(const Value : Cardinal); -begin - if FCommandTimeout <> Value then - begin - FCommandTimeout := Value; - if Connected then - Check(Engine, Engine.SetCommandTimeout(FHandle,FCommandTimeout)); - end; -end; - -procedure TPSQLDatabase.SetHost(const Value : string); -begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - if IsValidIP(Value) then - begin - FParams.Values['hostaddr'] := Value; - FParams.Values['host'] := ''; - end - else - begin - FParams.Values['hostaddr'] := ''; - FParams.Values['host'] := Value; - end; -end; - -procedure TPSQLDatabase.SetUseSSL(Reader: TReader); -begin - Reader.ReadBoolean(); //just ignore old property -end; - -procedure TPSQLDatabase.SetUserName(const Value : string); -begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FParams.Values['user'] := Value; -end; - -procedure TPSQLDatabase.SetUserPassword(const Value : string); -begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - - FParams.Values['password'] := Value; -end; - -procedure TPSQLDatabase.SetHandle(Value: DAChDBIDb); -begin - if Connected then Close; - if Value <> nil then - begin - FHandle := Value; - SetDatabaseFlags; - FAcquiredHandle := TRUE; - end; -end; - -procedure TPSQLDatabase.SetKeepConnection(Value: Boolean); -begin - if FKeepConnection <> Value then - FKeepConnection := Value; -end; - -procedure TPSQLDatabase.SetParams(Value: TStrings); -begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FParams.Assign(Value); -end; - -procedure TPSQLDatabase.SetDummyStr(Value: string); -begin -//dummy method for published -end; - -procedure TPSQLDatabase.SetDummyBool(Value: boolean); -begin -//dummy method for published -end; - -procedure TPSQLDatabase.SetDummyInt(Value: cardinal); -begin -//dummy method for published -end; - -procedure TPSQLDatabase.SetExclusive(Value: Boolean); -begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FExclusive := Value; -end; - -procedure TPSQLDatabase.SetReadOnly(Value: Boolean); -begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FReadOnly := Value; -end; - -function TPSQLDatabase.Engine : TPSQLEngine; -begin - Result := FEngine; -end; - -procedure TPSQLDatabase.GetStoredProcNames(Pattern: string; List: TStrings); -begin - List.BeginUpdate; - try - if Handle = nil then Connected := True; - List.Clear; - Check(Engine, Engine.OpenStoredProcList(Handle, Pattern, List)); - finally - List.EndUpdate; - end; -end; - -procedure TPSQLDatabase.GetTableNames(Pattern: string; SystemTables: Boolean; List: TStrings); -begin - List.BeginUpdate; - try - if Handle = nil then Connected := True; - List.Clear; - Check(Engine, Engine.OpenTableList(Handle, Pattern, SystemTables, List)); - finally - List.EndUpdate; - end; -end; - -procedure TPSQLDatabase.GetSchemaNames(Pattern: string; SystemSchemas: Boolean; List: TStrings); -begin - if not Assigned(List) then Exit; - List.BeginUpdate; - try - if Handle = nil then Connected := True; - List.Clear; - Check(Engine, Engine.OpenSchemaList(Handle, Pattern, SystemSchemas, List)); - finally - List.EndUpdate; - end; -end; - -function TPSQLDatabase.GetUsrName: string; -begin - Result := FParams.Values['user']; -end; - -procedure TPSQLDatabase.GetUserNames(Pattern: string; List: TStrings); -begin - List.BeginUpdate; - try - if Handle = nil then Connected := True; - List.Clear; - Check(Engine, Engine.OpenUserList(Handle,Pattern, List)); - finally - List.EndUpdate; - end; -end; - -function TPSQLDatabase.GetUserPassword: string; -begin - Result := FParams.Values['password']; -end; - -procedure TPSQLDatabase.GetCharsets(List: TStrings); -begin - List.BeginUpdate; - try - Check(Engine,Engine.GetCharacterSets(Handle,List)); - finally - List.EndUpdate; - end; -end; - -procedure TPSQLDatabase.SetCharSet(CharSet: string); -begin - if FCharSet <> CharSet then - begin - FCharSet := CharSet; - if Connected then - Engine.SetCharacterSet(Handle,CharSet) - end; -end; - -procedure TPSQLDatabase.GetDatabases(Pattern: string; List : TStrings); -var - OldConn : Boolean; - OldDbName : string; -begin - OldConn := Connected; - OldDbName := ''; - if not Connected then - begin - OldDbName := DatabaseName; - DatabaseName := 'template1'; - end; - if Handle = nil then Connected := True; - if Pattern <> '' then - Check(Engine, Engine.GetDatabases(Handle, Pattern, List)) else - Check(Engine, Engine.GetDatabases(Handle, '', List)); - Connected := OldConn; - if not Connected then - DatabaseName := OldDbName; -end; - -function TPSQLDatabase.GetNotifyItem(Index: Integer): TObject; -begin - Result := FNotifyList[Index]; -end; - -function TPSQLDatabase.GetNotifyCount: Integer; -begin - Result := FNotifyList.Count; -end; - -procedure TPSQLDatabase.AddNotify(AItem: TObject); -begin - if FNotifyList.IndexOf(AItem) >= 0 then Exit; - FNotifyList.Add(AItem); -end; - -procedure TPSQLDatabase.RemoveNotify(AItem: TObject); -var - I: Integer; -begin - I := FNotifyList.IndexOf(AItem); - if I >= 0 then - begin - try - TPSQLNotify(FNotifyList[I]).CloseNotify; - finally - FNotifyList.Delete(I); - end; - end; -end; - -procedure TPSQLDatabase.CloseNotify; -var - I: Integer; -begin - for I := 0 to FNotifyList.Count-1 do - TPSQLNotify(FNotifyList[I]).CloseNotify; -end; - -function TPSQLDatabase.GetBackendPID:Integer; -begin - Result := InvalidOID; - if Connected then - Engine.GetBackendPID(Handle, Result); -end; - - -////////////////////////////////////////////////////////// -//Constructor : TPSQLBDECallBack.Create -//Description : TPSQLBDECallBack -////////////////////////////////////////////////////////// -//Input : Engine: TPSQLEngine -// AOwner: TObject -// Handle: hDBICur -// CBType: CBType -// CBBuf: Pointer -// CBBufSize: Integer -// CallbackEvent: TPSQLBDECallbackEvent -// Chain: Boolean -////////////////////////////////////////////////////////// -constructor TPSQLBDECallBack.Create(Engine : TPSQLEngine; AOwner: TObject; Handle: hDBICur; CBType: CBType; - CBBuf: Pointer; CBBufSize: Integer; CallbackEvent: TPSQLBDECallbackEvent; - Chain: Boolean); -begin - FEngine := Engine; - FOwner := AOwner; - FHandle := Handle; - FCBType := CBType; - FCallbackEvent := CallbackEvent; -end; - -////////////////////////////////////////////////////////// -//Destructor : TPSQLBDECallBack.Destroy -//Description : Destroy TPSQLBDECallback -////////////////////////////////////////////////////////// -destructor TPSQLBDECallBack.Destroy; -begin - Inherited Destroy; -end; - -////////////////////////////////////////////////////////// -//function : TPSQLBDECallBack.Invoke -//Description : Invoke method TPSQLBDECallback -////////////////////////////////////////////////////////// -//Input : CallType: CBType -// CBInfo: Pointer -//Output : Result: CBRType -////////////////////////////////////////////////////////// -function TPSQLBDECallBack.Invoke(CallType: CBType; CBInfo: Pointer): CBRType; -begin - if (CallType = FCBType) then - Result := FCallbackEvent(CBInfo) - else - Result := cbrUSEDEF; - if Assigned(FOldCBFunc) then - Result := FOldCBFunc(CallType, FOldCBData, CBInfo); -end; - - -procedure TPSQLDatabase.GetTablespaces(Pattern: string; List: TStrings); -begin - if not Assigned(List) then Exit; - List.BeginUpdate; - try - if Handle = nil then Connected := True; - List.Clear; - Check(Engine, Engine.OpenTablespaceList(Handle, Pattern, List)); - finally - List.EndUpdate; - end; -end; - -procedure TPSQLDatabase.SetErrorVerbosity(const Value: TErrorVerbosity); -begin - if FErrorVerbosity <> Value then - begin - FErrorVerbosity := Value; - if Connected then - Engine.SetErrorVerbosity(Handle, Value) - end; -end; - -procedure TPSQLDatabase.SetSSLMode(const Value: TSSLMode); -begin - if FSSLMode <> Value then - begin - if FCheckIfActiveOnParamChange then - CheckInactive; //SSH tunneling - FSSLMode := Value; - FParams.Values['sslmode'] := SSLConsts[FSSLMode]; - end; -end; - -procedure TPSQLDatabase.Reset; -begin - CheckActive; - Check(Engine, Engine.Reset(FHandle)); -end; - -procedure TPSQLDatabase.CancelBackend(PID: Integer); -begin - CheckActive; - Check(Engine,Engine.CancelBackend(Handle,PID)); -end; - -procedure TPSQLDatabase.RegisterDirectQuery(aDirectQuery: TObject); -begin - FDirectQueryList.Add(aDirectQuery); -end; - -procedure TPSQLDatabase.UnregisterDirectQuery(aDirectQuery: TObject); -var - i : integer; -begin - i := FDirectQueryList.IndexOf(aDirectQuery); - if i <> -1 then - FDirectQueryList.Delete(i); -end; - -function TPSQLDatabase.SelectString(aSQL: string; var IsOk: boolean; - aFieldNumber: integer): string; -begin - DoConnect; - Check(Engine, Engine.SelectStringDirect(FHandle, PChar(aSQL), IsOk, Result, aFieldNumber)); -end; - -function TPSQLDatabase.SelectString(aSQL: string; var IsOk: boolean; - aFieldName: string): string; -begin - DoConnect; - Check(Engine, Engine.SelectStringDirect(FHandle, PChar(aSQL), IsOk, Result, aFieldName)); -end; - -procedure TPSQLDatabase.SelectStrings(aSQL: string; aList: TStrings; aFieldName: string); -begin - DoConnect; - Check(Engine, Engine.SelectStringsDirect(FHandle, PChar(aSQL), aList, aFieldName)); -end; - -procedure TPSQLDatabase.SelectStrings(aSQL: string; aList: TStrings; aFieldNumber: integer = 0); -begin - DoConnect; - Check(Engine, Engine.SelectStringsDirect(FHandle, PChar(aSQL), aList, aFieldNumber)); -end; - -function TPSQLDatabase.SelectStringDef(aSQL, aDefaultValue: string; - aFieldNumber: integer): string; -var - IsOk : boolean; -begin - Result := SelectString(aSQL, IsOk, aFieldNumber); - if not IsOk then - Result := aDefaultValue; -end; - -function TPSQLDatabase.SelectStringDef(aSQL, aDefaultValue, - aFieldName: string): string; -var - IsOk : boolean; -begin - Result := SelectString(aSQL, IsOk, aFieldName); - if not IsOk then - Result := aDefaultValue; -end; - -procedure TPSQLDatabase.FillAddonInfo; -begin - if not Connected or (FDatabaseID > 0) then Exit; - Engine.GetDBProps(FHandle, GetDatabaseName(), FOwner, FTablespace, - FIsTemplate,FDatabaseId, FComment); -end; - -function TPSQLDatabase.GetDatabaseComment: string; -begin - FillAddonInfo; - Result := FComment; -end; - -function TPSQLDatabase.GetDatabaseID: cardinal; -begin - FillAddonInfo; - Result := FDatabaseID; -end; - -function TPSQLDatabase.GetDatabaseName: string; -begin - Result := FParams.Values['dbname']; -end; - -function TPSQLDatabase.GetIsSSLUsed: Boolean; -begin - if Assigned(FHandle) then - Result := TNativeConnect(FHandle).IsSSLUsed - else - Result := False; -end; - -function TPSQLDatabase.GetIsTemplate: boolean; -begin - FillAddonInfo; - Result := FIsTemplate; -end; - -function TPSQLDatabase.GetIsUnicodeUsed: Boolean; -begin - if Assigned(FHandle) then - Result := TNativeConnect(FHandle).IsUnicodeUsed - else - Result := False; -end; - -function TPSQLDatabase.GetLibraryVersionAsInt: integer; -begin - if IsLibraryLoaded() then - Result := PQLibVersion() - else - Result := InvalidOID; -end; - -function TPSQLDatabase.GetDbOwner: string; -begin - FillAddonInfo; - Result := FOwner; -end; - -function TPSQLDatabase.GetGUCParamValue(const Name: string): string; -begin - if Assigned(FHandle) then - Result := TNativeConnect(FHandle).GUC.Values[Name] - else - Result := ''; -end; - -function TPSQLDatabase.GetHost: string; -begin - Result := FParams.Values['hostaddr']; - if Result = '' then - Result := FParams.Values['host']; -end; - -function TPSQLDatabase.GetTablespace: string; -begin - FillAddonInfo; - Result := FTablespace; -end; - -{ TPSQLDataSet } -procedure TPSQLDataSet.ReadByteaOpt(Reader: TReader); //deal with old missing properties -begin - if Reader.ReadBoolean then Include(FOptions, dsoByteaAsEscString); -end; - -procedure TPSQLDataSet.ReadOIDOpt(Reader: TReader); //deal with old missing properties -begin - if Reader.ReadBoolean then Include(FOptions, dsoOIDAsInt); -end; - -procedure TPSQLDataSet.DefineProperties(Filer: TFiler); -begin - inherited; - Filer.DefineProperty('ByteaAsEscString', ReadByteaOpt, nil, False); //missing - Filer.DefineProperty('OIDAsInt', ReadOIDOpt, nil, False); //missing -end; - -constructor TPSQLDataSet.Create(AOwner : TComponent); -var I: integer; -begin - Inherited Create(AOwner); - FCacheBlobs := False; - FAutoRefresh := FALSE; - FAllowSequenced := False; //Added by Nicolas Ring - -{$IFNDEF DELPHI_25} - FOptions := [dsoUseGUIDField]; -{$ENDIF} - - {$IFDEF MOBILE} - SetLength(FSetToRecBookm, SizeOf(TBookMark)); - {$ENDIF} - - if (csDesigning in ComponentState) and Assigned(AOwner) and (DBList.Count > 0) then - begin - for I := DBList.Count - 1 downto 0 do - if TCustomConnection(DBList[I]).Owner = AOwner then - begin - Database := TPSQLDatabase(DBList[I]); - Break; - end; - if not Assigned(Database) then - Database := TPSQLDatabase(DBList[DBList.Count - 1]); - end; -end; - -destructor TPSQLDataSet.Destroy; -begin - Inherited Destroy; - if FBlockReadBuf <> nil then - begin - FreeMem(FBlockReadBuf); - FBlockReadBuf := nil; - end; - SetUpdateObject(nil); -end; - -////////////////////////////////////////////////////////// -//procedure : TPSQLDataSet.OpenCursor -//Description : Open cursor -////////////////////////////////////////////////////////// -//Input : InfoQuery: Boolean -////////////////////////////////////////////////////////// -procedure TPSQLDataSet.OpenCursor(InfoQuery: Boolean); -var - I: Integer; -begin - if not Assigned(Database) then - DatabaseError('Property Database not set!', Self); - if dsoFetchOnDemand in Options then - for I := Database.DataSetCount - 1 downto 0 do - if Database.DataSets[I].Active then - DatabaseError('Cannot swith to fetch-on-demand mode since database object is used by another dataset', Self); - if not Assigned(FHandle) then - FHandle := CreateHandle(); - if not Assigned(FHandle) then - raise ENoResultSet.Create(SHandleError); - SetDBFlag(dbfOpened, TRUE); - Inherited OpenCursor(InfoQuery); - SetUpdateMode(FUpdateMode); - {$IFNDEF FPC} - SetupAutoRefresh; - {$ENDIF} -end; - -////////////////////////////////////////////////////////// -//procedure : TPSQLDataSet.CloseCursor -//Description : Close cursor -////////////////////////////////////////////////////////// -procedure TPSQLDataSet.CloseCursor; -begin - Inherited CloseCursor; - if FHandle <> nil then - begin - DestroyHandle; - FHandle := nil; - end; - FParentDataSet := nil; - SetDBFlag(dbfOpened, FALSE); -end; - -{$IFDEF DELPHI_12} -{$WARNINGS OFF} -procedure TPSQLDataset.CreateFields; -var F: TField; - I: integer; -begin - inherited CreateFields; - if FieldDefs.Count > Fields.Count then - for I := 0 to FieldDefs.Count - 1 do - if (FieldDefs[I].DataType = ftUnknown) and - not ((faHiddenCol in FieldDefs[I].Attributes) and not FieldDefs.HiddenFields) then - begin - case (FieldDefs[I] as TPSQLFieldDef).NativeDataType of - FIELD_TYPE_POINT: F := TPSQLPointField.Create(Self); - FIELD_TYPE_CIRCLE: F := TPSQLCircleField.Create(Self); - FIELD_TYPE_BOX: F := TPSQLBoxField.Create(Self); - FIELD_TYPE_LSEG: F := TPSQLLSegField.Create(Self); - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: F := TPSQLRangeField.Create(Self); - else - Continue; - end; - try - F.FieldName := FieldDefs[I].Name; - F.Required := faRequired in FieldDefs[I].Attributes; - F.ReadOnly := faReadonly in FieldDefs[I].Attributes; - F.DataSet := FieldDefs.DataSet; - F.Index := I; - except - F.Free; - raise; - end; - end; -end; -{$WARNINGS ON} -{$ENDIF DELPHI_12} - -////////////////////////////////////////////////////////// -//function : TPSQLDataSet.CreateHandle -//Description : Virtual method Create Handle will be overwritten -// in TPSQLQuery and TPSQLTable -////////////////////////////////////////////////////////// -//Output : Result: HDBICur -////////////////////////////////////////////////////////// -function TPSQLDataSet.CreateHandle: HDBICur; -begin - Result := nil; -end; - -procedure TPSQLDataSet.DestroyHandle; -begin - Engine.CloseCursor(FHandle); -end; - -procedure TPSQLDataSet.InternalInitFieldDefs; -var - I, FieldID: Integer; - FieldDescs: TFLDDescList; - ValCheckDesc: VCHKDesc; - RequiredFields: TBits; - CursorProps: CurProps; - FldDescCount, - MaxFieldID, - HiddenFieldCount: Integer; -begin - Engine.GetCursorProps(FHandle, CursorProps); - FldDescCount := CursorProps.iFields; - HiddenFieldCount := 0; - if FieldDefs.HiddenFields then - begin - if SetBoolProp(Engine, Handle, curGETHIDDENCOLUMNS, TRUE) then - begin - Engine.GetCursorProps(FHandle, CursorProps); - HiddenFieldCount := CursorProps.iFields - FldDescCount; - FldDescCount := CursorProps.iFields; - end; - end; - RequiredFields := TBits.Create; - try - MaxFieldID := GetIntProp(Engine, Handle, curMAXFIELDID); - if MaxFieldID > 0 then - RequiredFields.Size := MaxFieldID + 1 else - RequiredFields.Size := FldDescCount + 1; - for I := 1 to CursorProps.iValChecks do - begin - Engine.GetVChkDesc(FHandle, I, ValCheckDesc); - if ValCheckDesc.bRequired and not ValCheckDesc.bHasDefVal then - RequiredFields[ValCheckDesc.iFldNum] := True; - end; - SetLength(FieldDescs, FldDescCount); - Engine.GetFieldDescs(FHandle, FieldDescs); - FieldID := {$IFNDEF FPC}FieldNoOfs{$ELSE}1{$ENDIF}; - I := FieldID - 1; - FieldDefs.Clear; - while I < FldDescCount do - begin - FieldID := FieldDescs[I].iFldNum; - AddFieldDesc(FieldDescs, I, FieldID, RequiredFields, FieldDefs); - end; - if FieldDefs.HiddenFields then - begin - SetBoolProp(Engine, Handle, curGETHIDDENCOLUMNS, False); - if HiddenFieldCount > 0 then - for I := FldDescCount - HiddenFieldCount to FldDescCount - 1 do - FieldDefs[I].Attributes := FieldDefs[I].Attributes + [faHiddenCol]; - end; - finally - RequiredFields.Free; - end; -end; - -{$IFNDEF FPC} -procedure TPSQLDataSet.GetObjectTypeNames(Fields: TFields); -var - Len: integer; - I: Integer; - TypeDesc: ObjTypeDesc; - ObjectField: TObjectField; -begin - for I := 0 to Fields.Count-1 do - if Fields[I] is TObjectField then - begin - ObjectField := TObjectField(Fields[I]); - TypeDesc.iFldNum := ObjectField.FieldNo; - if (Engine.GetEngProp(hDBIObj(Handle), curFIELDTYPENAME, @TypeDesc, - SizeOf(TypeDesc), Len) = DBIERR_NONE) and (Len > 0) then - ObjectField.ObjectType := string(TypeDesc.szTypeName); - with ObjectField do - if DataType in [ftADT, ftArray] then - begin - if (DataType = ftArray) and SparseArrays and - (Fields[0].DataType = ftADT) then - GetObjectTypeNames(TObjectField(Fields[0]).Fields) else - GetObjectTypeNames(Fields); - end; - end -end; -{$ENDIF} - -procedure TPSQLDataSet.InternalOpen; -var - CursorProps: CurProps; -begin - Engine.GetCursorProps(FHandle, CursorProps); - FRecordSize := CursorProps.iRecBufSize; - BookmarkSize := CursorProps.iBookmarkSize; - FCanModify := (CursorProps.eOpenMode = dbiReadWrite) and not CursorProps.bTempTable; - FRecNoStatus := TRecNoStatus(CursorProps.ISeqNums); - FieldDefs.Updated := FALSE; - FieldDefs.Update; - GetIndexInfo; -{$WARNINGS OFF} - if (dsoForceCreateFields in FOptions) or DefaultFields then - CreateFields; -{$WARNINGS ON} - BindFields(TRUE); - {$IFNDEF FPC} - if ObjectView then GetObjectTypeNames(Fields); - {$ENDIF} - if (dsoPopulateFieldsOrigin in FOptions) then PopulateFieldsOrigin(); - InitBufferPointers(FALSE); - AllocKeyBuffers; - Engine.SetToBegin(FHandle); - PrepareCursor; - if Filter <> '' then FExprFilter := CreateExprFilter(Filter, FilterOptions, 0); - if Assigned(OnFilterRecord) then FFuncFilter := CreateFuncFilter(@TPSQLDataSet.RecordFilter, 1); - if Filtered then ActivateFilters; -end; - -procedure TPSQLDataSet.InternalClose; -begin - FFuncFilter := nil; - FExprFilter := nil; - FreeKeyBuffers; - BindFields(FALSE); - {$IFNDEF DELPHI_20}if DefaultFields then{$ENDIF} DestroyFields; - FIndexFieldCount := 0; - FKeySize := 0; - FExpIndex := FALSE; - FCaseInsIndex := FALSE; - FCanModify := FALSE; -end; - -procedure TPSQLDataSet.PrepareCursor; -begin -end; - -function TPSQLDataSet.IsCursorOpen: Boolean; -begin - Result := Handle <> nil; -end; - -procedure TPSQLDataSet.InternalHandleException; -var - O: TObject; -begin - if not Assigned(FDatabase) then Exit; - O := ExceptObject; - if not Assigned(O) then Exit; - if (O is Exception) and not (O is EAbort) and Assigned(FDatabase.FOnException) then - FDatabase.FOnException(Self, Exception(O)) -end; - -//////////////////////////////////////////////////////////// -// Record functions // -//////////////////////////////////////////////////////////// -procedure TPSQLDataSet.InitBufferPointers(GetProps: Boolean); -var - CursorProps: CurProps; -begin - if GetProps then - begin - Check(Engine, Engine.GetCursorProps(FHandle, CursorProps)); - BookmarkSize := CursorProps.iBookmarkSize; - FRecordSize := CursorProps.iRecBufSize; - end; - FBlobCacheOfs := FRecordSize + CalcFieldsSize; - FRecInfoOfs := FBlobCacheOfs + BlobFieldCount * SizeOf(Pointer); - FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo); - FRecBufSize := FBookmarkOfs + BookmarkSize; -end; - -{$IFDEF NEXTGEN} -function TPSQLDataSet.AllocRecBuf: TRecBuf; -begin - Result := NativeInt(AllocMem(FRecBufSize)); -end; - -procedure TPSQLDataSet.FreeRecBuf(var Buffer: TRecBuf); -begin - TArray(Buffer) := nil; -end; - -{$ELSE} - -function TPSQLDataSet.AllocRecordBuffer: TRecordBuffer; -begin - Result := AllocMem(FRecBufSize); -end; - -procedure TPSQLDataSet.FreeRecordBuffer(var Buffer : TRecordBuffer); -begin - Engine.CheckBuffer(FHandle, Buffer); //pasha_golub 10.08.06 - ClearBlobCache(Buffer); - FreeMem(Buffer); - Buffer := nil; -end; -{$ENDIF NEXTGEN} - -procedure TPSQLDataSet.InternalInitRecord(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); -begin - Engine.InitRecord(FHandle, Pointer(Buffer)); -end; - -procedure TPSQLDataSet.ClearBlobCache(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); -var - I: Integer; - addr: NativeUInt; -begin - if FCacheBlobs then - begin - addr := NativeUInt(Buffer) + NativeUInt(FBlobCacheOfs); - for I := 0 to Pred(BlobFieldCount) do - TBlobDataArray(addr)[ I ] := {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}; - end; -end; - -procedure TPSQLDataSet.ClearCalcFields(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); -begin - {$IFDEF DELPHI_12} - FillChar(PByte(Buffer)[FRecordSize], CalcFieldsSize, 0) - {$ELSE} - FillChar(Buffer[FRecordSize], CalcFieldsSize, 0); - {$ENDIF} -end; - -procedure TPSQLDataSet.InitRecord(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); -begin - {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED OFF}{$ENDIF} - Inherited InitRecord(Buffer); - {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED ON}{$ENDIF} - ClearBlobCache(Buffer); - with PRecInfo(Buffer + FRecInfoOfs)^ do - begin - UpdateStatus := TUpdateStatus(usInserted); - BookMarkFlag := bfInserted; - RecordNumber := -1; - end; -end; - -function TPSQLDataSet.GetRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; GetMode: TGetMode; DoCheck: Boolean): TGetResult; -var - Status: DBIResult; -begin - case GetMode of - gmCurrent: Status := Engine.GetRecord(FHandle, dbiNoLock, {$IFNDEF NEXTGEN}Buffer{$ELSE}Pointer(Buffer){$ENDIF}, @FRecProps); - gmNext: Status := Engine.GetNextRecord(FHandle, dbiNoLock, {$IFNDEF NEXTGEN}Buffer{$ELSE}Pointer(Buffer){$ENDIF}, @FRecProps); - gmPrior: Status := Engine.GetPriorRecord(FHandle, dbiNoLock, {$IFNDEF NEXTGEN}Buffer{$ELSE}Pointer(Buffer){$ENDIF}, @FRecProps); - else - Status := DBIERR_NONE; - end; - case Status of - DBIERR_NONE: - begin - with PRecInfo({$IFDEF NEXTGEN}DACPointerInt{$ENDIF}(Buffer) + FRecInfoOfs)^ do - begin - UpdateStatus := TUpdateStatus(FRecProps.iRecStatus); - BookmarkFlag := bfCurrent; - case FRecNoStatus of - rnParadox: RecordNumber := FRecProps.iSeqNum; - rnDBase: RecordNumber := FRecProps.iPhyRecNum; - else - RecordNumber := -1; - end; - end; - ClearBlobCache(Buffer); - {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED OFF}{$ENDIF} - GetCalcFields(Buffer); - {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED ON}{$ENDIF} - Check(Engine, Engine.GetBookmark(FHandle, {$IFNDEF NEXTGEN}Buffer{$ELSE}PByte(Buffer){$ENDIF} + FBookmarkOfs)); - Result := grOK; - end; - DBIERR_BOF: Result := grBOF; - DBIERR_EOF: Result := grEOF; - else - Result := grError; - if DoCheck then Check(Engine, Status); - end; -end; - -function TPSQLDataSet.GetCurrentRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}): Boolean; -begin - if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then - begin - UpdateCursorPos; - Result := (Engine.GetRecord(FHandle, dbiNoLock, Pointer(Buffer), nil) = DBIERR_NONE); - end else - Result := FALSE; -end; - -function TPSQLDataSet.GetOldRecord: PAnsiDACChar; -begin - UpdateCursorPos(); - - if SetBoolProp(Engine, Handle, curDELAYUPDGETOLDRECORD, TRUE) then - try - AllocCachedUpdateBuffers(True); - Check(Engine, Engine.GetRecord(FHandle, dbiNoLock, FUpdateCBBuf.pOldRecBuf, nil)); - Result := PAnsiDACChar(FUpdateCBBuf.pOldRecBuf); - AllocCachedUpdateBuffers(False); - finally - SetBoolProp(Engine, Handle, curDELAYUPDGETOLDRECORD, FALSE); - end else - Result := nil; -end; - -procedure TPSQLDataSet.FetchAll; -begin - if not EOF then - begin - CheckBrowseMode; - Check(Engine, Engine.SetToEnd(Handle)); - Check(Engine, Engine.GetPriorRecord(FHandle, dbiNoLock, nil, nil)); - CursorPosChanged; - end; -end; - -procedure TPSQLDataSet.FlushBuffers; -begin - CheckBrowseMode; -end; - -function TPSQLDataSet.GetRecordCount: Integer; -begin - CheckActive; - if Engine.GetRecordCount(FHandle, Result) <> DBIERR_NONE then - Result := -1; -end; - -function TPSQLDataSet.GetRecNo: Integer; -var - BufPtr: TRecordBuffer; -begin - CheckActive; - if (State = dsCalcFields) then - BufPtr := TRecordBuffer(CalcBuffer) - else - BufPtr := TRecordBuffer(ActiveBuffer); - Result := PRecInfo(BufPtr + FRecInfoOfs).RecordNumber; -end; - -procedure TPSQLDataSet.SetRecNo(Value : Integer); -begin - CheckBrowseMode; - if (FRecNoStatus = rnParadox) and (Value <> RecNo) then - begin - DoBeforeScroll; - if Engine.SetToSeqNo(Handle, Value) = DBIERR_NONE then - begin - Resync([rmCenter]); - DoAfterScroll; - end; - end; -end; - -function TPSQLDataSet.GetRecordSize: integer; -begin - Result := FRecordSize; -end; - -function TPSQLDataSet.GetActiveRecBuf(var RecBuf: TRecordBuffer): Boolean; -begin - case State of - dsBlockRead: - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(FBlockReadBuf + (FBlockBufOfs * FRecordSize)); - {$ELSE} - RecBuf := FBlockReadBuf + (FBlockBufOfs * FRecordSize); - {$ENDIF} - - dsBrowse: if IsEmpty then - Pointer(RecBuf) := nil - else - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(ActiveBuffer); - {$ELSE} - RecBuf := ActiveBuffer; - {$ENDIF} - - dsEdit, dsInsert: - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(ActiveBuffer); - {$ELSE} - RecBuf := ActiveBuffer; - {$ENDIF} - - dsSetKey: - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(PByte(FKeyBuffer) + SizeOf(TKeyBuffer)); - {$ELSE} - RecBuf := PAnsiChar(FKeyBuffer) + SizeOf(TKeyBuffer); - {$ENDIF} - - dsCalcFields: - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(CalcBuffer); - {$ELSE} - RecBuf := CalcBuffer; - {$ENDIF} - - dsFilter: - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(FFilterBuffer); - {$ELSE} - RecBuf := FFilterBuffer; - {$ENDIF} - - dsNewValue: if FInUpdateCallback then - Pointer(RecBuf) := FUpdateCBBuf.pNewRecBuf - else - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(ActiveBuffer); - {$ELSE} - RecBuf := ActiveBuffer; - {$ENDIF} - - dsOldValue: if FInUpdateCallback then - Pointer(RecBuf) := FUpdateCBBuf.pOldRecBuf - else - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(GetOldRecord); - {$ELSE} - RecBuf := GetOldRecord; - {$ENDIF} - - else - Pointer(RecBuf) := nil; - end; - Result := Pointer(RecBuf) <> nil; -end; - -procedure TPSQLDataSet.AddFieldDesc(FieldDescs: TFLDDescList; var DescNo: Integer; - var FieldID: Integer; RequiredFields: TBits; FieldDefs: TFieldDefs); -var - FType: TFieldType; -{$IFDEF DELPHI_12} - ANativeType: cardinal; -{$ENDIF} - FSize: integer; - FRequired: Boolean; - FPrecision, I: Integer; - FieldName, FName: string; - FieldDesc: FLDDesc; -begin - FieldDesc := FieldDescs[DescNo]; - Inc(DescNo); - with FieldDesc do - begin - FieldName := szName; - {$IFDEF DELPHI_12}ANativeType := iNativeType;{$ENDIF} - I := 0; - FName := FieldName; - while FieldDefs.IndexOf(string(FName)) >= 0 do - begin - Inc(I); - FName := Format('%s_%d', [string(FieldName), I]); - end; - if iFldType < MAXLOGFLDTYPES then - FType := DataTypeMap[iFldType] - else - FType := ftUnknown; - FSize := 0; - FPrecision := 0; - if RequiredFields.Size > FieldID then - FRequired := RequiredFields[FieldID] else - FRequired := False; - case iFldType of - fldZSTRING, fldBYTES, fldVARBYTES, fldADT, fldArray, fldRef: - begin - FSize := iUnits1; - end; - fldINT16, fldUINT16: - if iLen <> 2 then FType := ftUnknown; - fldINT32: - if iSubType = fldstAUTOINC then - begin - FType := ftAutoInc; - FRequired := False; - end; - fldFLOAT: - if iSubType = fldstMONEY then FType := ftCurrency; - fldBCD - {$IFDEF DELPHI_12} - , fldFmtBCD - {$ENDIF}: - begin - FSize := Abs(iUnits2); - FPrecision := iUnits1; - end; - fldBLOB: - begin - FSize := iUnits1; - if (iSubType >= fldstMEMO) and (iSubType <= fldstBFILE) then - FType := BlobTypeMap[iSubType]; - end; - fldUUID: - begin - FSize := PSQLTypes.UUIDLEN; - FType := ftGuid; - end; - end; - - //pg: Unicode playing - {$IFDEF DELPHI_12} - if TNativeConnect(FDatabase.Handle).IsUnicodeUsed then - case FType of - ftString: FType := ftWideString; - ftMemo: FType := ftWideMemo; - ftFixedChar: FType := ftFixedWideChar; - end; - {$ENDIF} - - - with FieldDefs.AddFieldDef {$IFDEF DELPHI_12}as TPSQLFieldDef{$ENDIF} do - begin - {$IFNDEF FPC}FieldNo := FieldID;{$ENDIF} - Inc(FieldID); - Name := FName; - {$IFDEF DELPHI_12}NativeDataType := ANativeType;{$ENDIF} - DataType := FType; - Size := FSize; - Precision := FPrecision; - if FRequired then - Attributes := [faRequired]; - if efldrRights = fldrREADONLY then - Attributes := Attributes + [faReadonly]; - if iSubType = fldstFIXED then - Attributes := Attributes + [faFixed]; - InternalCalcField := bCalcField; - case FType of - ftADT: - begin - if iSubType = fldstADTNestedTable then - Attributes := Attributes + [faUnNamed]; - for I := 0 to iUnits1 - 1 do - AddFieldDesc(FieldDescs, DescNo, FieldID, RequiredFields, {$IFNDEF FPC}ChildDefs{$ELSE}nil{$ENDIF}); - end; - ftArray: - begin - I := FieldID; - FieldDescs[DescNo].szName := FieldDesc.szName + '[0]'; - AddFieldDesc(FieldDescs, DescNo, I, RequiredFields, {$IFNDEF FPC}ChildDefs{$ELSE}nil{$ENDIF}); - Inc(FieldID, iUnits2); - end; - end; - end; - end; -end; - -{$IFNDEF FPC} -function TPSQLDataSet.GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer; -var - RecBuf: TRecordBuffer; - Status: DBIResult; - DoCheck: Boolean; -begin - Result := 0; - DoCheck := (BlockReadSize = 0); - if (BlockReadSize > 0) then - {$IFDEF DELPHI_12} - RecBuf := TRecordBuffer(FBlockReadBuf + (FBlockBufOfs * FRecordSize)) - {$ELSE} - RecBuf := FBlockReadBuf + (FBlockBufOfs * FRecordSize) - {$ENDIF} - else - if not GetActiveRecBuf(RecBuf) then Exit; - Status := Engine.OpenBlob(FHandle, Pointer(RecBuf), FieldNo, dbiReadOnly); - if (Status <> DBIERR_NONE) then - Exit; - try - Status := Engine.GetBlobSize(FHandle, Pointer(RecBuf), FieldNo, Result); - if (Status <> DBIERR_NONE) or (Result = 0) then Exit; - if (High(Buffer) <= Result) then - SetLength(Buffer, Trunc(Result + Result div 4)); - Status := Engine.GetBlob(FHandle, Pointer(RecBuf), FieldNo, 0, Result, Buffer, Result); - finally - if (Status <> DBIERR_NONE) then - Result := 0; - Engine.FreeBlob(FHandle, Pointer(RecBuf), FieldNo); - if DoCheck then - Check(Engine, Status) - end; -end; -{$ENDIF} - -{$IFDEF DELPHI_12} -function TPSQLDataSet.GetFieldClass(FieldDef: TFieldDef): TFieldClass; -begin - if FieldDef.DataType = ftUnknown then - case TPSQLFieldDef(FieldDef).NativeDataType of - FIELD_TYPE_POINT: Result := TPSQLPointField; - FIELD_TYPE_CIRCLE: Result := TPSQLCircleField; - FIELD_TYPE_BOX: Result := TPSQLBoxField; - FIELD_TYPE_LSEG: Result := TPSQLLSegField; - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: Result := TPSQLRangeField; - else - Result := inherited GetFieldClass(FieldDef); - end - else - Result := inherited GetFieldClass(FieldDef); -end; -{$ENDIF} - -{$IFNDEF NEXTGEN} -function TPSQLDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; -var - IsBlank: Boolean; - RecBuf: TRecordBuffer; - Status: DBIResult; -begin -{$IFNDEF FPC} - if (BlockReadSize > 0) then - begin - Status := Engine.GetField(FHandle, FieldNo, FBlockReadBuf + - (FBlockBufOfs * FRecordSize), Buffer, IsBlank); - Result := (Status = DBIERR_NONE) and not IsBlank; - end - else -{$ENDIF} - begin - Result := GetActiveRecBuf(RecBuf); - if Result then - begin - Check(Engine, Engine.GetField(FHandle, FieldNo, RecBuf, Buffer, IsBlank)); - Result := not IsBlank; - end - end; -end; - -function TPSQLDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean; -var - RecBuf: TRecordBuffer; -begin - if (Field.FieldNo > 0) then - Result := GetFieldData(Field.FieldNo, Buffer) - else - begin - if (State = dsBlockRead) then - begin - RecBuf := TRecordBuffer(TempBuffer); - Result := TRUE; - end - else - Result := GetActiveRecBuf(RecBuf); - if Result and (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsBlockRead]) then - begin - Inc(RecBuf, FRecordSize + Field.Offset); - Result := Boolean(RecBuf[ 0 ]); - if Result and (Buffer <> nil) then - Move(RecBuf[1], Buffer^, Field.DataSize); - end; - end; -end; - -{$ENDIF} - -{$IFDEF DELPHI_12} -function TPSQLDataSet.GetFieldDefsClass: TFieldDefsClass; -begin - Result := TPSQLFieldDefs; -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -function TPSQLDataSet.GetFieldData(Field: TField; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; -var - RecBuf: TRecordBuffer;//PByte; -begin - if Field.FieldNo > 0 then - Result := GetFieldData(Field.FieldNo, Buffer) - else - begin - if State = dsBlockRead then - begin - RecBuf := {PByte}TRecordBuffer(TempBuffer); - Result := True; - end else - Result := GetActiveRecBuf(RecBuf); - if Result and (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsBlockRead]) then - begin - Result := Boolean(PByte(RecBuf)[FRecordSize + Field.Offset]); - if Result and (Buffer <> nil) then - Move(PByte(RecBuf)[FRecordSize + Field.Offset + 1], Buffer[0], Field.DataSize); - end; - end; -end; - -function TPSQLDataSet.GetFieldData(FieldNo: Integer; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; -var - IsBlank: Boolean; - RecBuf: TRecordBuffer;//PByte; - Status: DBIResult; -begin - if BlockReadSize > 0 then - begin - { Optimized for speed. If error, just return false } - Status := Engine.GetField(FHandle, FieldNo, PByte(FBlockReadBuf) + - (FBlockBufOfs * FRecordSize), Buffer, IsBlank); - Result := (Status = DBIERR_NONE) and not IsBlank; - end else - begin - Result := GetActiveRecBuf(RecBuf); - if Result then - begin - Check(Engine, Engine.GetField(FHandle, FieldNo, Pointer(RecBuf), Buffer, IsBlank)); - Result := not IsBlank; - end - end; -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLDataSet.SetFieldData(Field: TField; Buffer: TValueBuffer); -var - RecBuf: TRecordBuffer;//PByte; -begin - if not (State in dsWriteModes) then DatabaseError(SNotEditing, Self); - if (State = dsSetKey) and ((Field.FieldNo < 0) or (FIndexFieldCount > 0) and - not Field.IsIndexField) then DatabaseErrorFmt(SNotIndexField, [Field.DisplayName]); - GetActiveRecBuf(RecBuf); - if Field.FieldNo > 0 then - begin - if State = dsCalcFields then DatabaseError(SNotEditing, Self); - if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then - DatabaseErrorFmt(SFieldReadOnly, [Field.DisplayName]); - Field.Validate(Buffer); - if Field.FieldKind <> fkInternalCalc then - Check(Engine, Engine.PutField(FHandle, Field.FieldNo, Pointer(RecBuf), @Buffer[0])); - end - else {fkCalculated, fkLookup} - begin - Boolean(PByte(RecBuf)[FRecordSize + Field.Offset]) := NativeUInt(Buffer) > 0; //was LongBool(Buffer) - if Boolean(PByte(RecBuf)[FRecordSize + Field.Offset]) then - Move(Buffer[0], PByte(RecBuf)[FRecordSize + Field.Offset + 1], Field.DataSize); - end; - if not (State in [dsCalcFields, dsFilter, dsNewValue]) then - DataEvent(deFieldChange, TDataEventInfo(Field)); -end; -{$ENDIF} - -{$IFNDEF NEXTGEN} -procedure TPSQLDataSet.SetFieldData(Field: TField; Buffer: Pointer); -var - RecBuf: TRecordBuffer; -{$IFDEF DELPHI_17} - AValueBuffer: TValueBuffer; -{$ENDIF} -begin - with Field do - begin - if not (State in dsWriteModes) then - DatabaseError(SNotEditing, Self); - if (State = dsSetKey) and ((FieldNo < 0) or (FIndexFieldCount > 0) and - not IsIndexField) then - DatabaseErrorFmt(SNotIndexField, [DisplayName], Self); - GetActiveRecBuf(RecBuf); - if (FieldNo > 0) then - begin - if (State = dsCalcFields) then DatabaseError(SNotEditing); - if ReadOnly and not (State in [dsSetKey, dsFilter]) then - DatabaseErrorFmt(SFieldReadOnly, [DisplayName]); - {$IFDEF DELPHI_17} - AValueBuffer := TValueBuffer(Buffer); - Validate(AValueBuffer); - {$ELSE} - Validate(Buffer); - {$ENDIF} - if FieldKind <> fkInternalCalc then - Check(Engine, Engine.PutField(FHandle, FieldNo, RecBuf, Buffer)); - end - else {fkCalculated, fkLookup} - begin - Inc(RecBuf, FRecordSize + Offset); - Boolean(RecBuf[0]) := LongBool(Buffer); - if Boolean(RecBuf[ 0 ]) then - Move(Buffer^, RecBuf[ 1 ], DataSize); - end; - if not (State in [dsCalcFields, dsFilter, dsNewValue]) then - DataEvent(deFieldChange, TDataEventInfo(Field)); - end; -end; -{$ENDIF} - -function TPSQLDataSet.GetBlobData(Field : TField; Buffer : TRecordBuffer) : TBlobData; -begin - Result := TBlobDataArray(Buffer + FBlobCacheOfs)[ Field.Offset ]; -end; - -procedure TPSQLDataSet.SetBlobData(Field : TField; Buffer : TRecordBuffer; Value : TBlobData); -var - addr: NativeUInt; -begin - if (Buffer = TRecordBuffer(ActiveBuffer)) then - begin - addr := NativeUInt(Buffer) + NativeUInt(FBlobCacheOfs); - TBlobDataArray(addr)[ Field.Offset ] := Value; - end; -end; - -procedure TPSQLDataSet.CloseBlob(Field: TField); -begin - Engine.FreeBlob(Handle, Pointer(ActiveBuffer), Field.FieldNo); -end; - -{$IFNDEF FPC} -function TPSQLDataSet.GetStateFieldValue(State: TDataSetState; Field: TField): Variant; -var Param: TPSQLParam; -begin - CheckCachedUpdateMode; - if State = dsOldValue then - begin - Param := TPSQLParam.Create(nil); - try - Engine.GetFieldOldValue(Handle, Field.FieldName, Param); - Result := Param.Value; - finally - Param.Free; - end; - end - else - Result := Inherited GetStateFieldValue(State, Field); -end; - -procedure TPSQLDataSet.SetStateFieldValue(State: TDataSetState; Field: TField; Const Value: Variant); -begin - CheckCachedUpdateMode; - Inherited SetStateFieldValue(State, Field, Value); -end; - -function TPSQLDataSet.GetFieldFullName(Field : TField) : string; -begin - Result := inherited GetFieldFullName(Field); -end; -{$ENDIF} - -procedure TPSQLDataSet.InternalFirst; -begin - Check(Engine, Engine.SetToBegin(FHandle)); -end; - -procedure TPSQLDataSet.InternalLast; -begin - Check(Engine, Engine.SetToEnd(FHandle)); -end; - -procedure TPSQLDataSet.InternalEdit; -begin - FOldBuffer := {$IFNDEF NEXTGEN}AllocRecordBuffer{$ELSE}AllocRecBuf{$ENDIF}; - Move(Pointer(ActiveBuffer)^, {$IFDEF NEXTGEN}PByte{$ENDIF}(FOldBuffer)[0], FRecBufSize); - Check(Engine, Engine.GetRecord(FHandle, dbiWriteLock, Pointer(ActiveBuffer), nil)); - ClearBlobCache({$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}(ActiveBuffer)); -end; - -procedure TPSQLDataSet.InternalInsert; -begin - SetBoolProp(Engine, Handle, curMAKECRACK, TRUE); - CursorPosChanged; -end; - -procedure TPSQLDataSet.InternalPost; -begin - if Assigned(FUpdateObject) then - begin - if State = dsEdit then - FUpdateObject.Apply(ukModify) - else - if State = dsInsert then - FUpdateObject.Apply(ukInsert); - end //if assigned - else - begin - {$IFDEF DELPHI_6} - inherited; //mi:2008-02-13 Moved from begining of the method. Actually there is only CheckRequiredFields there. - // So we don't need it if dataset is being updated by UpdateObject - {$ENDIF}//pasha_golub 10.08.06 - - if State = dsEdit then - Check(Engine, Engine.ModifyRecord(FHandle, Pointer(FOldBuffer), Pointer(ActiveBuffer), TRUE,RecNo)) - else - if State = dsInsert then - Check(Engine, Engine.InsertRecord(FHandle, dbiNoLock, Pointer(ActiveBuffer))); - end; //else - if assigned(Pointer(fOldBuffer)) then - {$IFNDEF NEXTGEN} - FreeRecordBuffer(FOldBuffer); - {$ELSE} - FreeRecBuf(FOldBuffer); - {$ENDIF} -end; - -procedure TPSQLDataSet.InternalDelete; -var - Result: Word; -begin - if not Assigned(FUpdateObject) then - begin - Result := Engine.DeleteRecord(FHandle, Pointer(ActiveBuffer)); - if (Result <> DBIERR_NONE) then Check(Engine, Result); - end - else - FUpdateObject.Apply(ukDelete); -end; - -function TPSQLDataSet.IsSequenced: Boolean; -begin - Result := (FRecNoStatus = rnParadox) and (not Filtered); -end; - -function TPSQLDataSet.GetCanModify: Boolean; -begin - Result := FCanModify or ForceUpdateCallback; -end; - -procedure TPSQLDataSet.InternalRefresh; -begin - Check(Engine, Engine.ForceReread(FHandle)); -end; - -procedure TPSQLDataSet.Post; -begin - Inherited Post; - if (State = dsSetKey) then - PostKeyBuffer(TRUE); -end; - -procedure TPSQLDataSet.Cancel; -begin - Inherited Cancel; - if State = dsSetKey then - PostKeyBuffer(FALSE); -end; - -procedure TPSQLDataSet.InternalCancel; -begin - if State = dsEdit then - Engine.RelRecordLock(FHandle, FALSE); - if assigned(Pointer(fOldBuffer)) then - {$IFNDEF NEXTGEN} - FreeRecordBuffer(FOldBuffer); - {$ELSE} - FreeRecBuf(fOldBuffer); - {$ENDIF} -end; - -procedure TPSQLDataSet.InternalAddRecord(Buffer: {$IFNDEF NEXTGEN}Pointer{$ELSE}TRecBuf{$ENDIF}; Append: Boolean); -begin - if Append then - Check(Engine, Engine.AppendRecord(FHandle, Pointer(Buffer))) else - Check(Engine, Engine.InsertRecord(FHandle, dbiNoLock, Pointer(Buffer))); -end; - -{$IFDEF NEXTGEN} -procedure TPSQLDataSet.InternalGotoBookmark(Bookmark: TBookmark); -begin - Check(Engine, Engine.SetToBookmark(FHandle, Bookmark)); -end; -{$ELSE} -procedure TPSQLDataSet.InternalGotoBookmark(Bookmark : Pointer); -begin - Check(Engine, Engine.SetToBookmark(FHandle, Bookmark)); -end; -{$ENDIF} - -procedure TPSQLDataSet.InternalSetToRecord(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); -begin -{$IFNDEF NEXTGEN} - InternalGotoBookmark(Pointer(Buffer + FBookmarkOfs)); -{$ELSE} - Move(Pointer(DACPointerInt(Buffer) + FBookmarkOfs)^, FSetToRecBookm[0], SizeOf(TBookMark)); - InternalGotoBookmark(FSetToRecBookm); -{$ENDIF} -end; - -function TPSQLDataSet.GetBookmarkFlag(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}) : TBookmarkFlag; -begin - Result := PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag; -end; - -procedure TPSQLDataSet.SetBookmarkFlag(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Value : TBookmarkFlag); -begin - PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag := Value; -end; - -{$IFNDEF NEXTGEN} -procedure TPSQLDataSet.GetBookmarkData(Buffer : TRecordBuffer; Data : Pointer); -begin - Move(Buffer[FBookmarkOfs], Data^, BookmarkSize); -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLDataSet.GetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); -begin - Move(PByte(Buffer)[FBookmarkOfs], Data[0], BookmarkSize); -end; -{$ENDIF DELPHI_17} - -{$IFNDEF NEXTGEN} -procedure TPSQLDataSet.SetBookmarkData(Buffer : TRecordBuffer; Data : Pointer); -begin - Move(Data^, Buffer[FBookmarkOfs], BookmarkSize); -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLDataSet.SetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); -begin - Move(Data[0], PByte(Buffer)[FBookmarkOfs], BookmarkSize); -end; -{$ENDIF DELPHI_17} - -function TPSQLDataSet.CompareBookmarks(Bookmark1, Bookmark2 : TBookmark) : Integer; -const - RetCodes: array[Boolean, Boolean] of ShortInt = ((2,CMPLess),(CMPGtr,CMPEql)); -begin - { Check for uninitialized bookmarks } - Result := RetCodes[Bookmark1 = nil, Bookmark2 = nil]; - if (Result = 2) then - begin - if (Handle <> nil) then - Check(Engine, Engine.CompareBookmarks(Handle, Bookmark1, Bookmark2, Result)); - if (Result = CMPKeyEql) then - Result := CMPEql; - end; -end; - -function TPSQLDataSet.BookmarkValid(Bookmark: TBookmark): Boolean; -begin - Result := (Handle <> nil); - if Result then - begin - CursorPosChanged; - Result := (Engine.SetToBookmark(FHandle, Bookmark) = DBIERR_NONE) and - (Engine.GetRecord(FHandle, dbiNOLOCK, nil, nil) = DBIERR_NONE) - end; -end; - -{$IFNDEF FPC} -procedure TPSQLDataSet.SetBlockReadSize(Value: Integer); - - function CanBlockRead: Boolean; - var - i: Integer; - begin - Result := (BufferCount <= 1) and (DataSetField = nil); - if Result then - for i := 0 to FieldCount - 1 do - if (Fields[i].DataType in [ftDataSet, ftReference]) then - begin - Result := False; - break; - end; - end; - - procedure FreeBuffer; - begin - if FBlockReadBuf <> nil then - begin - FreeMem(FBlockReadBuf); - FBlockReadBuf := nil; - end; - end; - -const - DEFBLOCKSIZE = 64 * 1024; -var - Size: Integer; -begin - if Value <> BlockReadSize then - begin - if Value > 0 then - begin - if EOF or not CanBlockRead then Exit; - FreeBuffer; - UpdateCursorPos; - -//mi:2008-03-25 #0766 curMAKECRACK flag set native dataset to tsEmpty mode. But we need to set it in tsFirst mode! -// Engine.SetEngProp(HDBIObj(FHandle), curMAKECRACK, 0); - TNativeDataSet(FHandle).RecordState := tsFirst; - - if Value = MaxInt then - Size := DEFBLOCKSIZE - else - Size := Value * FRecordSize; - - FBlockReadBuf := AllocMem(Size); - FBlockBufSize := Size div FRecordSize; - FBlockBufOfs := FBlockBufSize; { Force read of data } - FBlockBufCount := FBlockBufSize; - FBlockReadCount := 0; - - inherited; - - BlockReadNext(); - end - else - begin - inherited; -// CursorPosChanged; -// Resync([]); - FreeBuffer; - end; - end; -end; - -procedure TPSQLDataSet.BlockReadNext; -var - Status: DbiResult; -begin - if FBlockBufOfs >= FBlockBufCount - 1 then - begin - if FBlockBufCount < FBlockBufSize then - Last() - else - begin - Status := Engine.ReadBlock(FHandle, FBlockBufCount, FBlockReadBuf); - - if (Status <> DBIERR_NONE) and (Status <> DBIERR_EOF) then - Check(Engine,Status); - - if (FBlockBufCount = 0) and (Status = DBIERR_EOF) then - Last(); - Inc(FBlockReadCount, FBlockBufCount); - FBlockBufOfs := 0; - end - end - else - Inc(FBlockBufOfs); - - if CalcFieldsSize > 0 then - GetCalcFields(TempBuffer); - - DataEvent(deDataSetScroll, -1); -end; -{$ENDIF} - -procedure TPSQLDataSet.GetIndexInfo; -var - IndexDesc: IDXDesc; -begin - if Engine.GetIndexDesc(FHandle, 0, IndexDesc) = DBIERR_NONE then - begin - FExpIndex := IndexDesc.bExpIdx; - FCaseInsIndex := IndexDesc.bCaseInsensitive; - if not ExpIndex then - begin - FIndexFieldCount := IndexDesc.iFldsInKey; - FIndexFieldMap := IndexDesc.aiKeyFld; - end; - FKeySize := IndexDesc.iKeyLen; - end; -end; - - -procedure TPSQLDataSet.SwitchToIndex(const IndexName, TagName : string); -var - Status: DBIResult; -begin - ResetCursorRange; - UpdateCursorPos; - Status := Engine.SwitchToIndex(FHandle, IndexName, TagName, 0, TRUE); - if (Status = DBIERR_NOCURRREC) then - Status := Engine.SwitchToIndex(FHandle, IndexName, TagName, 0, FALSE); - Check(Engine, Status); - FKeySize := 0; - FExpIndex := FALSE; - FCaseInsIndex := FALSE; - FIndexFieldCount := 0; - SetBufListSize(0); - InitBufferPointers(TRUE); - try - SetBufListSize(BufferCount + 1); - except - SetState(dsInactive); - CloseCursor; - raise; - end; - GetIndexInfo; -end; - -function TPSQLDataSet.GetIndexField(Index : Integer): TField; -var - FieldNo: Integer; -begin - if (Index < 0) or (Index >= FIndexFieldCount) then DatabaseError(SFieldIndexError, Self); - FieldNo := FIndexFieldMap[Index]; - Result := FieldByNumber(FieldNo); - if Result = nil then DatabaseErrorFmt(SIndexFieldMissing, [ FieldDefs[FieldNo - 1].Name ], Self); -end; - -procedure TPSQLDataSet.SetIndexField(Index : Integer; Value : TField); -begin - GetIndexField(Index).Assign(Value); -end; - -function TPSQLDataSet.GetIndexFieldCount: Integer; -begin - Result := FIndexFieldCount; -end; - -procedure TPSQLDataSet.AllocKeyBuffers; -var - KeyIndex: TKeyIndex; -begin - try - for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do - FKeyBuffers[KeyIndex] := InitKeyBuffer(AllocMem(SizeOf(TKeyBuffer) + FRecordSize)); - except - FreeKeyBuffers; - raise; - end; -end; - -procedure TPSQLDataSet.FreeKeyBuffers; -var - KeyIndex: TKeyIndex; -begin - for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do - DisposeMem(FKeyBuffers[ KeyIndex ], SizeOf(TKeyBuffer) + FRecordSize); -end; - -function TPSQLDataSet.InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer; -begin - FillChar(Buffer^, SizeOf(TKeyBuffer) + FRecordSize, 0); - Engine.InitRecord(FHandle, PAnsiDACChar(Buffer) + SizeOf(TKeyBuffer)); - Result := Buffer; -end; - -procedure TPSQLDataSet.CheckSetKeyMode; -begin - if (State <> dsSetKey) then DatabaseError(SNotEditing, Self); -end; - -function TPSQLDataSet.SetCursorRange: Boolean; -var - RangeStart, RangeEnd: PKeyBuffer; - StartKey, EndKey: PAnsiDACChar; - IndexBuffer: PAnsiDACChar; - UseStartKey, UseEndKey, UseKey: Boolean; -begin - Result := FALSE; - if not (BuffersEqual(FKeyBuffers[kiRangeStart], FKeyBuffers[kiCurRangeStart],SizeOf(TKeyBuffer) + FRecordSize) and - BuffersEqual(FKeyBuffers[kiRangeEnd], FKeyBuffers[kiCurRangeEnd],SizeOf(TKeyBuffer) + FRecordSize)) then - begin - IndexBuffer := AllocMem(KeySize * 2); - try - UseStartKey := TRUE; - UseEndKey := TRUE; - RangeStart := FKeyBuffers[kiRangeStart]; - if RangeStart.Modified then - begin - StartKey := PAnsiDACChar(RangeStart) + SizeOf(TKeyBuffer); - UseStartKey := Engine.ExtractKey(Handle, StartKey, IndexBuffer) = 0; - end - else - StartKey := nil; - RangeEnd := FKeyBuffers[kiRangeEnd]; - if RangeEnd.Modified then - begin - EndKey := PAnsiDACChar(RangeEnd) + SizeOf(TKeyBuffer); - UseEndKey := (Engine.ExtractKey(Handle, EndKey, IndexBuffer + KeySize) = 0); - end - else - EndKey := nil; - UseKey := UseStartKey and UseEndKey; - if UseKey then - begin - if (StartKey <> nil) then - StartKey := IndexBuffer; - if (EndKey <> nil) then - EndKey := IndexBuffer + KeySize; - end; - Check(Engine, Engine.SetRange(FHandle, UseKey, - RangeStart.FieldCount, 0, StartKey, not RangeStart.Exclusive, - RangeEnd.FieldCount, 0, EndKey, not RangeEnd.Exclusive)); - Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiCurRangeStart]^, - SizeOf(TKeyBuffer) + FRecordSize); - Move(FKeyBuffers[kiRangeEnd]^, FKeyBuffers[kiCurRangeEnd]^, - SizeOf(TKeyBuffer) + FRecordSize); - Result := TRUE; - finally - FreeMem(IndexBuffer, KeySize * 2); - end; - end; -end; - -function TPSQLDataSet.ResetCursorRange: Boolean; -begin - Result := FALSE; - if FKeyBuffers[kiCurRangeStart].Modified or - FKeyBuffers[kiCurRangeEnd].Modified then - begin - Check(Engine, Engine.ResetRange(FHandle)); - InitKeyBuffer(FKeyBuffers[kiCurRangeStart]); - InitKeyBuffer(FKeyBuffers[kiCurRangeEnd]); - Result := TRUE; - end; -end; - -procedure TPSQLDataSet.SetLinkRanges(MasterFields: TList{$IFDEF DELPHI_17}{$ENDIF}); -var - I: Integer; - SaveState: TDataSetState; -begin - SaveState := SetTempState(dsSetKey); - try - FKeyBuffer := InitKeyBuffer(FKeyBuffers[kiRangeStart]); - FKeyBuffer^.Modified := TRUE; - for I := 0 to Pred(MasterFields.Count) do - GetIndexField(I).Assign(TField(MasterFields[I])); - FKeyBuffer^.FieldCount := MasterFields.Count; - finally - RestoreState(SaveState); - end; - Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiRangeEnd]^, - SizeOf(TKeyBuffer) + FRecordSize); -end; - -function TPSQLDataSet.GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer; -begin - Result := FKeyBuffers[KeyIndex]; -end; - -procedure TPSQLDataSet.SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean); -begin - CheckBrowseMode; - FKeyBuffer := FKeyBuffers[KeyIndex]; - Move(FKeyBuffer^, FKeyBuffers[kiSave]^, SizeOf(TKeyBuffer) + FRecordSize); - if Clear then InitKeyBuffer(FKeyBuffer); - SetState(dsSetKey); - SetModified(FKeyBuffer.Modified); - DataEvent(deDataSetChange, 0); -end; - -procedure TPSQLDataSet.PopulateFieldsOrigin(); -var I: integer; -begin - for I := 0 to Fields.Count -1 do - Fields[I].Origin := Engine.GetFieldOrigin(FHandle, Fields[I].FieldNo) -end; - -procedure TPSQLDataSet.PostKeyBuffer(Commit: Boolean); -begin - DataEvent(deCheckBrowseMode, 0); - if FKeyBuffer^.FieldCount = 0 then - FKeyBuffer^.FieldCount := FIndexFieldCount; - if Commit then - FKeyBuffer.Modified := Modified - else - Move(FKeyBuffers[kiSave]^, FKeyBuffer^, SizeOf(TKeyBuffer) + FRecordSize); - SetState(dsBrowse); - DataEvent(deDataSetChange, 0); -end; - -function TPSQLDataSet.GetKeyExclusive: Boolean; -begin - CheckSetKeyMode; - Result := FKeyBuffer.Exclusive; -end; - -procedure TPSQLDataSet.SetKeyExclusive(Value: Boolean); -begin - CheckSetKeyMode; - FKeyBuffer.Exclusive := Value; -end; - -function TPSQLDataSet.GetKeyFieldCount: Integer; -begin - CheckSetKeyMode; - Result := FKeyBuffer.FieldCount; -end; - -procedure TPSQLDataSet.SetKeyFieldCount(Value: Integer); -begin - CheckSetKeyMode; - FKeyBuffer.FieldCount := Value; -end; - -procedure TPSQLDataSet.SetKeyFields(KeyIndex: TKeyIndex; - const Values: array of const); -var - I: Integer; - SaveState: TDataSetState; -begin - if ExpIndex then - DatabaseError(SCompositeIndexError, Self); - if (FIndexFieldCount = 0) then - DatabaseError(SNoFieldIndexes, Self); - SaveState := SetTempState(dsSetKey); - try - FKeyBuffer := InitKeyBuffer(FKeyBuffers[KeyIndex]); - for I := 0 to High(Values) do - GetIndexField(I).AssignValue(Values[I]); - FKeyBuffer^.FieldCount := High(Values) + 1; - FKeyBuffer^.Modified := Modified; - finally - RestoreState(SaveState); - end; -end; - -function TPSQLDataSet.GetIsIndexField(Field: TField): Boolean; -var - I: Integer; -begin - Result := FALSE; - with Field do - if (FieldNo > 0) then - for I := 0 to Pred(FIndexFieldCount) do - if (FIndexFieldMap[I] = FieldNo) then - begin - Result := TRUE; - Exit; - end; -end; - -procedure TPSQLDataSet.Notification(AComponent: TComponent; Operation: TOperation); -begin - Inherited Notification(AComponent, Operation); - if (Operation = opRemove) and (AComponent = FDatabase) then - begin - Close; - FDatabase := nil; - end; -end; - -procedure TPSQLDataSet.ActivateFilters; -begin - if FExprFilter <> nil then - begin - if Engine.ActivateFilter(FHandle, FExprFilter) <> DBIERR_NONE then - begin - Engine.DropFilter(FHandle, FExprFilter); - FExprFilter := CreateExprFilter(Filter, FilterOptions, 0); - Check(Engine, Engine.ActivateFilter(FHandle, FExprFilter)); - end; - end; - if FFuncFilter <> nil then - begin - if (Engine.ActivateFilter(FHandle, FFuncFilter) <> DBIERR_NONE) then - begin - Engine.DropFilter(FHandle, FFuncFilter); - FFuncFilter := CreateFuncFilter(@TPSQLDataSet.RecordFilter, 1); - Check(Engine, Engine.ActivateFilter(FHandle, FFuncFilter)); - end; - end; -end; - -procedure TPSQLDataSet.DeactivateFilters; -begin - if FFuncFilter <> nil then Check(Engine, Engine.DeactivateFilter(FHandle, FFuncFilter)); - if FExprFilter <> nil then Check(Engine, Engine.DeactivateFilter(FHandle, FExprFilter)); -end; - -function TPSQLDataSet.CreateExprFilter(const Expr: string; - Options: TFilterOptions; Priority: Integer): HDBIFilter; -var - Parser: TExprParser; -begin - Parser := TExprParser.Create(Self, Expr, Options, [], '', nil, FldTypeMap); - try - Check(Engine, Engine.AddFilter(FHandle, 0, Priority, FALSE, PCANExpr(Parser.FilterData), nil, Result)); - finally - Parser.Free; - end; -end; - -function TPSQLDataSet.CreateFuncFilter(FilterFunc: Pointer;Priority: Integer): HDBIFilter; -begin - Check(Engine, Engine.AddFilter(FHandle, Integer(Self), Priority, FALSE, nil, PFGENFilter(FilterFunc), Result)); -end; - -function TPSQLDataSet.CreateLookupFilter(Fields: TList{$IFDEF NEXTGEN}{$ENDIF}; const Values: Variant; - Options: TLocateOptions; Priority: Integer): HDBIFilter; -var - I: Integer; - AFilter: TFilterExpr; - Expr, Node: PExprNode; - AFilterOptions: TFilterOptions; -begin - Node := nil; - Expr := nil; - if loCaseInsensitive in Options then - AFilterOptions := [foNoPartialCompare, foCaseInsensitive] - else - AFilterOptions := [foNoPartialCompare]; - AFilter := TFilterExpr.Create(Self, AFilterOptions, [], '', nil, FldTypeMap); - try - if (Fields.Count = 1) and not VarIsArray(Values) then - begin - Node := AFilter.NewCompareNode(TField(Fields[0]), coEQ, Values); - Expr := Node; - end - else - for I := 0 to Fields.Count-1 do - begin - Node := AFilter.NewCompareNode(TField(Fields[I]), coEQ, Values[I]); - if I = 0 then - Expr := Node else - Expr := AFilter.NewNode(enOperator, coAND, Unassigned, Expr, Node); - end; - if loPartialKey in Options then Node^.FPartial := TRUE; - Check(Engine, Engine.AddFilter(FHandle, 0, Priority, FALSE, PCANExpr(AFilter.GetFilterData(Expr)), nil, Result)); - finally - AFilter.Free; - end; -end; - -procedure TPSQLDataSet.SetFilterHandle(var Filter: HDBIFilter; Value: HDBIFilter); -begin - if Filtered then - begin - CursorPosChanged; - Engine.SetToBegin(FHandle); - if Filter <> nil then Engine.DropFilter(FHandle, Filter); - Filter := Value; - if Filter <> nil then Engine.ActivateFilter(FHandle, Filter); - end else - begin - if Filter <> nil then Engine.DropFilter(FHandle, Filter); - Filter := Value; - end; -end; - -procedure TPSQLDataSet.SetFilterData(const Text: string; Options: TFilterOptions); -var - HFilter: HDBIFilter; -begin - if Active then - begin - CheckBrowseMode; - if (Filter <> Text) or (FilterOptions <> Options) then - begin - if Text <> '' then - HFilter := CreateExprFilter(Text, Options, 0) else - HFilter := nil; - SetFilterHandle(FExprFilter, HFilter); - end; - end; - Inherited SetFilterText(Text); - Inherited SetFilterOptions(Options); - if Active and Filtered then First; -end; - -procedure TPSQLDataSet.SetFilterText(const Value: string); -begin - SetFilterData(Value, FilterOptions); -end; - -procedure TPSQLDataSet.SetFiltered(Value: Boolean); -begin - if Active then - begin - CheckBrowseMode; - if Filtered <> Value then - begin - Engine.SetToBegin(FHandle); - if Value then - ActivateFilters - else - DeactivateFilters; - inherited SetFiltered(Value); - end; - First; - end - else - inherited SetFiltered(Value); -end; - -procedure TPSQLDataSet.SetFilterOptions(Value: TFilterOptions); -begin - SetFilterData(Filter, Value); -end; - -procedure TPSQLDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent); -var - AFilter: HDBIFilter; -begin - if Active then - begin - CheckBrowseMode; - if Assigned(OnFilterRecord) <> Assigned(Value) then - begin - if Assigned(Value) then - AFilter := CreateFuncFilter(@TPSQLDataSet.RecordFilter, 1) else - AFilter := nil; - SetFilterHandle(FFuncFilter, AFilter); - end; - Inherited SetOnFilterRecord(Value); - if Filtered then - First; - end - else - Inherited SetOnFilterRecord(Value); -end; - -function TPSQLDataSet.FindRecord(Restart, GoForward: Boolean): Boolean; -var - Status: Word; -begin - CheckBrowseMode; - DoBeforeScroll; - SetFound(FALSE); - UpdateCursorPos; - CursorPosChanged; - if not Filtered then ActivateFilters; - try - if GoForward then - begin - if Restart then Check(Engine, Engine.SetToBegin(FHandle)); - Status := Engine.GetNextRecord(FHandle, dbiNoLock, nil, nil); - end - else - begin - if Restart then Check(Engine, Engine.SetToEnd(FHandle)); - Status := Engine.GetPriorRecord(FHandle, dbiNoLock, nil, nil); - end; - finally - if not Filtered then - DeactivateFilters; - end; - if Status = DBIERR_NONE then - begin - Resync([rmExact, rmCenter]); - SetFound(TRUE); - end; - Result := Found; - if Result then DoAfterScroll; -end; - -function TPSQLDataSet.RecordFilter(RecBuf: Pointer; RecNo: Integer): Smallint; -var - Accept: Boolean; - SaveState: TDataSetState; -begin - SaveState := SetTempState(dsFilter); - FFilterBuffer := RecBuf; - try - Accept := TRUE; - OnFilterRecord(Self, Accept); - except - {$IFNDEF FPC} - InternalHandleException(); - {$ENDIF} - end; - RestoreState(SaveState); - Result := Ord(Accept); -end; - -function TPSQLDataSet.LocateRecord(const KeyFields: string; - const KeyValues: Variant; - Options: TLocateOptions; - SyncCursor: Boolean): Boolean; -var - Fields: TList{$IFDEF DELPHI_17}{$ENDIF}; - CaseInsensitive: boolean; - Flds : array of integer; - SFlds : array of string; - i, FieldCount, R : integer; - aPartial : boolean; - Status : Word; -begin - if Self.Filtered then - begin - //mi:2009-07-31 we have to respect filters - Status := LocateFilteredRecord(KeyFields, KeyValues, Options, SyncCursor); - Result := Status = DBIERR_NONE; - Exit; - end; - - CheckBrowseMode(); - CursorPosChanged(); - DoBeforeScroll(); - - Result := False; - - Fields := TList{$IFDEF DELPHI_17}{$ENDIF}.Create; - try - GetFieldList(Fields, KeyFields); - CaseInsensitive := loCaseInsensitive in Options; - - FieldCount := Fields.Count; - - SetLength(Flds, FieldCount); - SetLength(SFlds, FieldCount); - - if FieldCount = 1 then - begin - Flds[0] := TField(Fields.First).FieldNo - 1; - if VarIsArray(KeyValues) then - SFlds[0] := VarToStr(KeyValues[0]) //mi:2009-12-22 #1270 thanks to Matija Vidmar - else - SFlds[0] := VarToStr(KeyValues); - end - else - for i := 0 to FieldCount - 1 do - begin - Flds[i] := TField(Fields[i]).FieldNo - 1; - SFlds[i] := VarToStr(KeyValues[i]) - end; - - aPartial := (loPartialKey in Options) and (TField(Fields.Last).DataType in [ftString, ftWideString]); - - R := TNativeDataSet(FHandle).FindRows(Flds, SFlds, not CaseInsensitive, 0, not aPartial); - - if R <> -1 then - begin - Result := True; - TNativeDataSet(FHandle).InitRecord(Pointer(ActiveBuffer)); - TNativeDataSet(FHandle).SetToRecord(R); - if SyncCursor then - Resync([rmExact, rmCenter]); - DoAfterScroll(); - end; - finally - Fields.Free(); - end; - -end; - -function TPSQLDataSet.LocateFilteredRecord(const KeyFields: string; - const KeyValues: Variant; - Options: TLocateOptions; - SyncCursor: Boolean): Word; -var - Fields: TList{$IFDEF DELPHI_17}{$ENDIF}; - AFilter: HDBIFilter; - Status: DBIResult; - I: Integer; - Filter1: TFilterExpr; - Expr, Node: PExprNode; - fo: TFilterOptions; - pos : int64; -begin - CheckBrowseMode(); - CursorPosChanged(); - DoBeforeScroll(); - - pos := TNativeDataSet(FHandle).RecordNumber; - - Fields := TList{$IFDEF DELPHI_17}{$ENDIF}.Create(); - try - GetFieldList(Fields, KeyFields); - Check(Engine, Engine.SetToBegin(FHandle)); - - fo := [foNoPartialCompare]; - - //mi:2010-02-07 - if loCaseInsensitive in Options then - fo := fo + [foCaseInsensitive]; - - Filter1 := TFilterExpr.Create(Self, fo, [], '', nil, FldTypeMap); - - try - Node := nil; - Expr := nil; - if Fields.Count = 1 then - begin - if VarIsArray(KeyValues) then - Node := Filter1.NewCompareNode(TField(Fields[0]), coEQ, KeyValues[0]) - else - Node := Filter1.NewCompareNode(TField(Fields[0]), coEQ, KeyValues); - - Expr := Node; - end - else - begin - for i := 0 to Fields.Count - 1 do - begin - Node := Filter1.NewCompareNode(TField(Fields[I]), coEQ, KeyValues[I]); - - if I = 0 then - Expr := Node - else - Expr := Filter1.NewNode(enOperator, coAND, Unassigned, Expr, Node); - end; - end; - - if loPartialKey in Options then - Node^.FPartial := TRUE; - - Check(Engine, Engine.AddFilter(FHandle, 0, 2, FALSE, PCANExpr(Filter1.GetFilterData(Expr)), nil, AFilter)); - finally - Filter1.Free(); - end; - - Engine.ActivateFilter(FHandle, AFilter); - Status := Engine.GetNextRecord(FHandle, dbiNoLock, Pointer(ActiveBuffer), nil); - Engine.DropFilter(FHandle, AFilter); - finally - Fields.Free(); - end; - - Result := Status; - - if SyncCursor then - begin - if Result = DBIERR_NONE then - begin - Resync([rmExact, rmCenter]); - end - else - begin -// TNativeDataSet(FHandle).InitRecord(ActiveBuffer); - TNativeDataSet(FHandle).SetToRecord(pos); - end; - - DoAfterScroll(); - end; -end; - -function TPSQLDataSet.LocateNearestRecord(const KeyFields: string;const KeyValues: Variant;Options: TLocateOptions;SyncCursor: Boolean): Word; -var - Buffer: TRecordBuffer; - Fields: TList{$IFDEF DELPHI_17}{$ENDIF}; - AFilter: HDBIFilter; - Status: DBIResult; - I: Integer; - Filter1: TFilterExpr; - Expr, Node: PExprNode; - AFilterOptions: TFilterOptions; - -begin - Expr := nil; //make compiler happy - Node := nil; //make compiler happy - CheckBrowseMode; - CursorPosChanged; - Buffer := Pointer(TempBuffer); - Fields := TList{$IFDEF DELPHI_17}{$ENDIF}.Create; - try - GetFieldList(Fields, KeyFields); - Check(Engine, Engine.SetToBegin(FHandle)); - AFilterOptions := [foNoPartialCompare]; - Filter1 := TFilterExpr.Create(Self, AFilterOptions, [], '', nil, FldTypeMap); - try - if Fields.Count = 1 then - begin - Node := Filter1.NewCompareNode(TField(Fields[0]), coGE, KeyValues); - Expr := Node; - end - else - for I := 0 to Fields.Count-1 do - begin - Node := Filter1.NewCompareNode(TField(Fields[I]), coGE, KeyValues[I]); - if I = 0 then - Expr := Node else - Expr := Filter1.NewNode(enOperator, coAND, Unassigned, Expr, Node); - end; - if loPartialKey in Options then Node^.FPartial := TRUE; - Check(Engine, Engine.AddFilter(FHandle, 0, 2, FALSE, PCANExpr(Filter1.GetFilterData(Expr)), nil,AFilter)); - finally - Filter1.Free; - end; - Engine.ActivateFilter(FHandle, AFilter); - Status := Engine.GetNextRecord(FHandle, dbiNoLock, Buffer, nil); - Engine.DropFilter(FHandle, AFilter); - finally - Fields.Free; - end; - Result := Status; -end; - -function IsSameVarArrays(A1, A2: variant): boolean; -var I: integer; -begin - Result := VarIsArray(A1) and VarIsArray(A2); - for I := VarArrayLowBound(A1, 1) to VarArrayHighBound(A2, 1) do - Result := Result AND (A1[i] = A2[i]); -end; - -function TPSQLDataSet.Lookup(const KeyFields: string; const KeyValues: Variant; - const ResultFields: string): Variant; -var OldPos: integer; - FVal: variant; -begin - Result := Null; - if VarIsNull(KeyValues) then Exit; - OldPos := RecNo; - DisableControls; - try - First; - while not Eof do - begin - FVal := FieldValues[KeyFields]; - if VarIsArray(FVal) and VarIsArray(KeyValues) then - begin - if IsSameVarArrays(FVal, KeyValues) then - Result := FieldValues[ResultFields] - end - else - if FVal = KeyValues then Result := FieldValues[ResultFields]; - if not VarIsNull(Result) then Exit; - Next; - end; - finally - if OldPos = 1 then First() else - if OldPos = RecordCount then Last() else RecNo := OldPos; - EnableControls; - end; -end; - -function TPSQLDataSet.Locate(const KeyFields: string; - const KeyValues: Variant; Options: TLocateOptions): Boolean; -begin - DoBeforeScroll(); - Result := LocateRecord(KeyFields, KeyValues, Options, True); -end; - -procedure TPSQLDataSet.AllocCachedUpdateBuffers(Allocate: Boolean); -begin - if Allocate then - begin - FUpdateCBBuf := AllocMem(SizeOf(DELAYUPDCbDesc)); - FUpdateCBBuf.pNewRecBuf := AllocMem(FRecBufSize); - FUpdateCBBuf.pOldRecBuf := AllocMem(FRecBufSize); - FUpdateCBBuf.iRecBufSize := FRecordSize; - end - else - begin - if Assigned(FUpdateCBBuf) then - begin - FreeMem(FUpdateCBBuf.pNewRecBuf); - FreeMem(FUpdateCBBuf.pOldRecBuf); - DisposeMem(FUpdateCBBuf, SizeOf(DELAYUPDCbDesc)); - end; - end; -end; - -procedure TPSQLDataSet.CheckCachedUpdateMode; -begin -end; - -function TPSQLDataSet.UpdateCallbackRequired: Boolean; -begin -{$IFDEF FPC} - Result := False; -{$ELSE} - Result := FCachedUpdates and (Assigned(FOnUpdateError) or - Assigned(FOnUpdateRecord) or Assigned(FUpdateObject)); -{$ENDIF} -end; - -function TPSQLDataSet.ForceUpdateCallback: Boolean; -begin - Result := True{FCachedUpdates} and ({$IFNDEF FPC}Assigned(FOnUpdateRecord) or{$ENDIF} - Assigned(FUpdateObject)); -end; - -procedure TPSQLDataSet.SetCachedUpdates(Value: Boolean); - - procedure ReAllocBuffers; - begin - FreeFieldBuffers; - FreeKeyBuffers; - SetBufListSize(0); - try - InitBufferPointers(TRUE); - SetBufListSize(BufferCount + 1); - AllocKeyBuffers; - except - SetState(dsInactive); - CloseCursor; - raise; - end; - end; - -begin - if (State = dsInActive) or (csDesigning in ComponentState) then - FCachedUpdates := Value - else - if (FCachedUpdates <> Value) then - begin - CheckBrowseMode; - UpdateCursorPos; - FCachedUpdates := Value; - ReAllocBuffers; - AllocCachedUpdateBuffers(Value); - SetupCallBack(UpdateCallBackRequired); - Resync([]); - end; -end; - -procedure TPSQLDataSet.SetupCallBack(Value: Boolean); -begin - if Value then - begin - if (csDesigning in ComponentState) then - Exit; - if not Assigned(FUpdateCallback) then - FUpdateCallback := TPSQLBDECallBack.Create(Engine, Self, Self.Handle, cbDELAYEDUPD, - FUpdateCBBuf, SizeOf(DELAYUPDCbDesc), CachedUpdateCallBack, TRUE); - end - else - begin - if Assigned(FUpdateCallback) then - begin - {$IFDEF NEXTGEN} - FUpdateCallback.DisposeOf; - {$ELSE} - FUpdateCallback.Free; - {$ENDIF} - FUpdateCallback := nil; - end; - end; -end; - -function TPSQLDataSet.ProcessUpdates(UpdCmd: DBIDelayedUpdCmd): Word; -begin - CheckCachedUpdateMode; - UpdateCursorPos; - Result :=0; -// Resync([]); //NEW -end; - -procedure TPSQLDataSet.ApplyUpdates; -var - Status: Word; -begin - if (State <> dsBrowse) then Post; - Status := ProcessUpdates(dbiDelayedUpdPrepare); - if (Status <> DBIERR_NONE) then - if (Status = DBIERR_UPDATEABORT) then SysUtils.Abort else TDbiError(Engine,Status); -end; - -procedure TPSQLDataSet.CommitUpdates; -begin - Check(Engine, ProcessUpdates(dbiDelayedUpdCommit)); -end; - -procedure TPSQLDataSet.CancelUpdates; -begin - Cancel; - ProcessUpdates(dbiDelayedUpdCancel); -end; - -procedure TPSQLDataSet.RevertRecord; -var - Status: Word; -begin - if State in dsEditModes then Cancel; - Status := ProcessUpdates(dbiDelayedUpdCancelCurrent); - if not ((Status = DBIERR_NONE) or (Status = DBIERR_NOTSUPPORTED)) then - Check(Engine, Status); -end; - - -function TPSQLDataSet.UpdateStatus: TUpdateStatus; -begin - Result := usUnModified; -end; - -function TPSQLDataSet.CachedUpdateCallBack(CBInfo: Pointer): CBRType; -const - CBRetCode: array[TUpdateAction] of CBRType = (cbrAbort, cbrAbort, - cbrSkip, cbrRetry, cbrPartialAssist); -var - UpdateAction: TUpdateAction; - UpdateKind: TUpdateKind; -begin - FInUpdateCallBack := TRUE; - UpdateAction := uaFail; - UpdateKind := TUpdateKind(ord(FUpdateCBBuf.eDelayUpdOpType)-1); - try -{$IFNDEF FPC} - if Assigned(FOnUpdateRecord) then - FOnUpdateRecord(Self, UpdateKind, UpdateAction) - else -{$ENDIF} - if Assigned(FUpdateObject) then - begin - FUpdateObject.Apply(UpdateKind); - UpdateAction := uaApplied; - end - else - TDbiError(Engine, FUpdateCBBuf.iErrCode); - except - on E: Exception do - begin - if E is EPSQLDatabaseError then - FUpdateCBBuf.iErrCode := EPSQLDatabaseError(E).ErrorCode; -{$IFNDEF FPC} - if (E is EDatabaseError) and Assigned(FOnUpdateError) then - FOnUpdateError(Self, EDatabaseError(E), UpdateKind, UpdateAction) - else -{$ENDIF} - begin - {$IFNDEF FPC} - InternalHandleException(); - {$ENDIF} - UpdateAction := uaAbort; - end; - end; - end; - Result := CBRetCode[UpdateAction]; - if UpdateAction = uaAbort then - FUpdateCBBuf.iErrCode := DBIERR_UPDATEABORT; - FInUpdateCallBack := FALSE; -end; - -{$IFNDEF FPC} -function TPSQLDataSet.GetUpdateRecordSet: TUpdateRecordTypes; -begin - if Active then - begin - Result := TUpdateRecordTypes(Byte(GetIntProp(Engine, FHandle, - curDELAYUPDDISPLAYOPT))); - end - else - Result := []; -end; - -procedure TPSQLDataSet.SetUpdateRecordSet(RecordTypes: TUpdateRecordTypes); -begin - CheckBrowseMode; - UpdateCursorPos; - Check(Engine, Engine.SetEngProp(hDbiObj(Handle), curDELAYUPDDISPLAYOPT, Longint(Byte(RecordTypes)))); - Resync([]); -end; -{$ENDIF} - -procedure TPSQLDataSet.SetUpdateObject(Value: TPSQLSQLUpdateObject); -begin - if (Value <> FUpdateObject) then - begin - if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then - FUpdateObject.DataSet := nil; - FUpdateObject := Value; - if Assigned(FUpdateObject) then - begin - { if another dataset already references this updateobject, then - remove the reference } - if Assigned(FUpdateObject.DataSet) and (FUpdateObject.DataSet <> Self) then - FUpdateObject.DataSet.UpdateObject := nil; - FUpdateObject.DataSet := Self; - end; - end; -end; - -{$IFNDEF FPC} -procedure TPSQLDataSet.SetOnUpdateError(UpdateEvent: TUpdateErrorEvent); -begin - if Active then SetupCallback(UpdateCallBackRequired); - FOnUpdateError := UpdateEvent; -end; -{$ENDIF} - -function TPSQLDataSet.GetUpdatesPending: Boolean; -begin - Result := GetIntProp(Engine, FHandle, curDELAYUPDNUMUPDATES) > 0; -end; - -//{$IFDEF DELPHI_17} -//procedure TPSQLDataSet.DataConvert(Field: TField; Source: TValueBuffer; {$IFDEF DELPHI_18}var{$ENDIF} Dest: TValueBuffer; ToNative: Boolean); -//begin -// if (Field.DataType = ftDateTime) and not ToNative then //#1871 TDateTimeField supports dates before 30/12/1899 from now -// Move(Source[0], Dest[0], SizeOf(TDateTime)) -// else -// inherited; -//end; -//{$ELSE} -//procedure TPSQLDataSet.DataConvert(Field: TField; Source, Dest: Pointer; ToNative: Boolean); -//begin -// if (Field.DataType = ftDateTime) and not ToNative then //#1871 TDateTimeField supports dates before 30/12/1899 from now -// TDateTime(Dest^) := TDateTime(Source^) -// else -// inherited; -//end; -//{$ENDIF} - -procedure TPSQLDataSet.DataEvent(Event: TDataEvent; Info: TDataEventInfo); - - procedure CheckIfParentScrolled; - var - ParentPosition, I: Integer; - begin - ParentPosition := 0; - with FParentDataSet do - if not IsEmpty then - for I := 0 to BookmarkSize - 1 do - ParentPosition := ParentPosition + Byte(TRecordBuffer(ActiveBuffer)[FBookmarkOfs+I]); - if (FLastParentPos = 0) or (ParentPosition <> FLastParentPos) then - begin - First; - FLastParentPos := ParentPosition; - end - else - begin - UpdateCursorPos; - Resync([]); - end; - end; - -begin - if (Event = deParentScroll) then - CheckIfParentScrolled; - inherited DataEvent(Event, Info); -end; - -{$IFNDEF FPC} -{ TBDEDataSet.IProviderSupport} -function TPSQLDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; -var - PrevErr: Integer; -begin - if E is EPSQLDatabaseError then - begin - if Prev <> nil then - PrevErr := Prev.ErrorCode else - PrevErr := 0; - with EPSQLDatabaseError(E) do - Result := EUpdateError.Create(E.Message, '', ErrorCode, PrevErr, E); - end - else - Result := inherited PSGetUpdateException(E, Prev); -end; - -function TPSQLDataSet.PSIsSQLSupported: Boolean; -begin - Result := TRUE; -end; - -procedure TPSQLDataSet.PSReset; -begin - inherited PSReset; - if Handle <> nil then - Engine.ForceReread(Handle); -end; -{$ENDIF} - -function TPSQLDataSet.GetHandle: HDBICur; -begin - Result := FHandle; -end; - - -function TPSQLDataSet.CheckOpen(Status: Word): Boolean; -begin - case Status of - DBIERR_NONE: Result := TRUE; - DBIERR_NOTSUFFTABLERIGHTS: Result := FALSE; - else - TDbiError(Engine, Status); - Result := FALSE; - end; -end; - -procedure TPSQLDataSet.Disconnect; -begin - Close; -end; - -function TPSQLDataSet.GetDBHandle: DAChDBIDb; -begin - if FDatabase <> nil then - begin - if FDatabase.Handle = nil then - FDatabase.Connected := True; - Result := FDatabase.Handle; - end - else - Result := nil; -end; - -procedure TPSQLDataSet.GetDatabaseNames(List : TStrings); -var - i : Integer; - Names : TStringList; -begin - Names := TStringList.Create; - try - Names.Sorted := TRUE; - for I := 0 to DBList.Count-1 do - with TPSQLDatabase(DBList[i]) do Names.Add(DatabaseName); - List.Assign(Names); - finally - Names.Free; - end; -end; - -procedure TPSQLDataSet.CloseDatabase(Database: TPSQLDatabase); -begin - if Assigned(Database) then - Database.CloseDatabase(Database); -end; - -function TPSQLDataSet.SetDBFlag(Flag: Integer; Value: Boolean): Boolean; -begin - Result := Flag in DBFlags; - if Value then - begin - if not Result then - begin - if FDBFlags = [] then - begin - FDatabase.Open; - Inc(FDatabase.FRefCount); - {$IFNDEF FPC} - FDatabase.RegisterClient(Self); - {$ENDIF} - end; - Include(FDBFlags, Flag); - end; - end - else - begin - if Result then - begin - Exclude(FDBFlags, Flag); - if FDBFlags = [] then - begin - {$IFNDEF FPC} - FDatabase.UnRegisterClient(Self); - {$ENDIF} - CloseDatabase(FDatabase); - end; - end; - end; -end; - -procedure TPSQLDataSet.SetUpdateMode(const Value: TUpdateMode); -begin - if (FHandle <> nil) and True and CanModify then - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle), curUPDLOCKMODE, Longint(Value))); - FUpdateMode := Value; -end; - -{ AutoRefresh } -procedure TPSQLDataSet.SetAutoRefresh(const Value: Boolean); -begin - CheckInactive; - FAutoRefresh := Value; -end; - -procedure TPSQLDataSet.SetDatabase(Value: TPSQLDatabase); -begin - if Active then Close; - try - {$IFNDEF FPC} - if Assigned(FDatabase) then FDatabase.UnRegisterClient(Self); - {$ENDIF} - if Assigned(Value) then FDatabase := Value; - finally - FDatabase := Value; - end; -end; - -function TPSQLDataSet.GetDatabase: TPSQLDatabase; -begin - Result := TPSQLDatabase(FDatabase); -end; - -{$IFNDEF FPC} -procedure TPSQLDataSet.SetupAutoRefresh; -const - PropFlags : array[TAutoRefreshFlag] of LongInt = (0, curFIELDISAUTOINCR, curFIELDISDEFAULT); -var - I : Integer; - ColDesc : ServerColDesc; -begin - if AutoRefresh then - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),curAUTOREFETCH,LongInt(TRUE))); - for I := 0 to Fields.Count - 1 do - with Fields[I] do - if (AutoGenerateValue <> arNone) then - begin - ColDesc.iFldNum := I + 1; - ColDesc.bServerCol := TRUE; - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle), PropFlags[ AutoGenerateValue ], LongInt(@ColDesc))); - end; -end; - -{ TPSQLDataSet.IProviderSupport } -procedure TPSQLDataSet.PSGetAttributes(List: {$IFDEF DELPHI_19}TPacketAttributeList{$ELSE}TList{$ENDIF}); -var - Attr: {$IFNDEF NEXTGEN}PPacketAttribute{$ELSE}TPacketAttribute{$ENDIF}; -begin - inherited PSGetAttributes(List); //29.11.2007 - {$IFNDEF NEXTGEN} - New(Attr); - {$ENDIF} - List.Add(Attr); - {$IFNDEF NEXTGEN} - with Attr^ do - {$ELSE} - with Attr do - {$ENDIF} - begin - Name := 'LCID'; - Value := Integer(-1); - IncludeInDelta := False; - end; -end; - -function TPSQLDataSet.PSIsSQLBased: Boolean; -var - InProvider : Boolean; -begin - InProvider := SetDBFlag(dbfProvider, TRUE); - try - Result := True; - finally - SetDBFlag(dbfProvider, InProvider); - end; -end; - -function TPSQLDataSet.PSGetQuoteChar: string; -begin - Result := '"'; -end; - -function TPSQLDataSet.PSInTransaction: Boolean; -var - InProvider: Boolean; -begin - if not Assigned(Database) or not Database.Connected then - Result := FALSE - else - begin - InProvider := SetDBFlag(dbfProvider, TRUE); - try - Result := Database.InTransaction; - finally - SetDBFlag(dbfProvider, InProvider); - end; - end; -end; - -procedure TPSQLDataSet.PSStartTransaction; -begin - SetDBFlag(dbfProvider, TRUE); - try - if not PSIsSQLBased then - Database.TransIsolation := tiDirtyRead; - Database.StartTransaction; - except - SetDBFlag(dbfProvider, FALSE); - Raise; - end; -end; - -procedure TPSQLDataSet.PSEndTransaction(Commit : Boolean); -const - EndType: array[Boolean] of eXEnd = (xendABORT, xendCOMMIT); -begin - try - Database.ClearStatements; - Database.EndTransaction(EndType[ Commit ]); - finally - SetDBFlag(dbfProvider, FALSE); - end; -end; - -{$IFDEF DELPHI_17} -function TPSQLDataSet.PSExecuteStatement(const ASQL: string; - AParams: TParams): Integer; -var - InProvider: Boolean; -begin - InProvider := SetDBFlag(dbfProvider, True); - try - Result := Database.Execute(ASQL, AParams); - finally - SetDBFlag(dbfProvider, InProvider); - end; -end; - -function TPSQLDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams; - var ResultSet: TDataSet): Integer; -var - InProvider: Boolean; -begin - Result := -1; - InProvider := SetDBFlag(dbfProvider, TRUE); - try - ResultSet := TPSQLQuery.Create(nil); - try - TPSQLQuery(ResultSet).Database := Database; - TPSQLQuery(ResultSet).SQL.Text := ASQL; - TPSQLQuery(ResultSet).Params.Assign(AParams); - TPSQLQuery(ResultSet).Open; - Result := Max(TPSQLQuery(ResultSet).RowsAffected, TPSQLQuery(ResultSet).RecordCount); - except - FreeAndNil(ResultSet); - raise; - end; - finally - SetDBFlag(dbfProvider, InProvider); - end; -end; -{$ELSE} -function TPSQLDataSet.PSExecuteStatement(const ASQL : string; AParams: TParams; ResultSet: Pointer = nil): Integer; -var - InProvider: Boolean; - Q: TPSQLQuery; -begin - InProvider := SetDBFlag(dbfProvider, TRUE); - try - if Assigned(ResultSet) or Assigned(AParams) then - begin - {$WARNINGS OFF} //make D5 compiler happy - Q := TPSQLQuery.Create(nil); - {$WARNINGS ON} - try - Q.Database := Database; - Q.SQL.Text := ASQL; - Q.Params.Assign(AParams); - // In Insert, Resolver expect the number of records affected... - if not Assigned(ResultSet) then - Q.ExecSQL - else - Q.Open; - Result := Q.RowsAffected; - finally - if Assigned(ResultSet) then - TPSQLDataset(ResultSet^) := Q - else - Q.Free; - end; - end - else - Result := Database.Execute(ASQL); - finally - SetDBFlag(dbfProvider, InProvider); - end; -end; -{$ENDIF DELPHI_17} -{$ENDIF FPC} - -///////////////////////////////////////////////////////////////// -// TPSQLQuery // -///////////////////////////////////////////////////////////////// -constructor TPSQLQuery.Create(AOwner: TComponent); -begin - Inherited Create(AOwner); - FSQL := TStringList.Create; - TStringList(SQL).OnChange := QueryChanged; - FParams := TPSQLParams.Create(Self); - FDataLink := TPSQLQueryDataLink.Create(Self); - RequestLive := FALSE; - ParamCheck := TRUE; - FRowsAffected := -1; - CacheBlobs := False; -end; - -destructor TPSQLQuery.Destroy; -begin - Destroying; - Disconnect; - FSQL.Free; - FParams.Free; - FDataLink.Free; - StrDispose(SQLBinary); - Inherited Destroy; -end; - -function TPSQLQuery.Engine : TPSQLEngine; -begin - Result := FDataBase.Engine; -end; - -function TPSQLQuery.CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; -begin - Result := TPSQLBlobStream.Create(Field as TBlobField, Mode); -end; - -function TPSQLQuery.IsSequenced: Boolean; -begin - Result := FAllowSequenced and inherited IsSequenced; -end; - -procedure TPSQLQuery.Disconnect; -begin - Close; - UnPrepare; -end; - -procedure TPSQLQuery.SetPrepare(Value: Boolean); -begin - if Value then - Prepare else UnPrepare; -end; - -procedure TPSQLQuery.Prepare; -begin - if Assigned(FHandle) then - begin - SetDBFlag(dbfPrepared, TRUE); - SetPrepared(TRUE); - end; -end; - -procedure TPSQLQuery.UnPrepare; -begin - SetPrepared(FALSE); - SetDBFlag(dbfPrepared, FALSE); -end; - -procedure TPSQLQuery.SetDataSource(Value: TDataSource); -begin - if IsLinkedTo(Value) then - DatabaseError(SCircularDataLink, Self); - FDataLink.DataSource := Value; -end; - -function TPSQLQuery.GetDataSource: TDataSource; -begin - Result := FDataLink.DataSource; -end; - -procedure TPSQLQuery.SetQuery(Value: TStrings); -begin - if SQL.Text <> Value.Text then - begin - Disconnect; - SQL.BeginUpdate; - try - SQL.Assign(Value); - finally - SQL.EndUpdate; - end; - end; -end; - -function TPSQLQuery.GetQuery:TStrings; -begin - Result := FSQL; -end; - -procedure TPSQLQuery.QueryChanged(Sender: TObject); -var - List: TPSQLParams; -begin - if not (csReading in ComponentState) then - begin - Disconnect; - StrDispose(SQLBinary); - SQLBinary := nil; - if ParamCheck {or (csDesigning in ComponentState)} then - begin - List := TPSQLParams.Create(Self); - try - FText := List.ParseSQL(SQL.Text, True); - List.AssignValues(FParams); - FParams.Clear; - FParams.Assign(List); - finally - List.Free; - end; - end else - FText := SQL.Text; - DataEvent(dePropertyChange, 0); - end else - FText := FParams.ParseSQL(SQL.Text, False); -end; - -procedure TPSQLQuery.SetParamsList(Value: TPSQLParams); -begin - FParams.AssignValues(Value); -end; - -function TPSQLQuery.GetParamsCount: integer; -begin - Result := FParams.Count; -end; - -procedure TPSQLQuery.DefineProperties(Filer: TFiler); - - function WriteData: Boolean; - begin - if (Filer.Ancestor <> nil) then - Result := not FParams.IsEqual(TPSQLQuery(Filer.Ancestor).FParams) - else - Result := (FParams.Count > 0); - end; - -begin - Inherited DefineProperties(Filer); - Filer.DefineBinaryproperty('Data', ReadBinaryData, WriteBinaryData, SQLBinary <> nil); - Filer.DefineProperty('ParamData', ReadParamData, WriteParamData, WriteData); -end; - -procedure TPSQLQuery.ReadParamData(Reader: TReader); -begin - Reader.ReadValue; - Reader.ReadCollection(FParams); -end; - -procedure TPSQLQuery.WriteParamData(Writer: TWriter); -begin - Writer.WriteCollection(Params); -end; - -procedure TPSQLQuery.ReadBinaryData(Stream: TStream); -begin - SQLBinary := StrAlloc(Stream.Size); - Stream.ReadBuffer(SQLBinary^, Stream.Size); -end; - -procedure TPSQLQuery.WriteBinaryData(Stream: TStream); -begin - Stream.WriteBuffer(SQLBinary^, StrBufSize(SQLBinary)); -end; - -procedure TPSQLQuery.SetRequestLive(const Value : Boolean); -begin - if Value <> FRequestLive then - FRequestLive := Value; -end; - -function TPSQLQuery.GetRequestLive : Boolean; -begin - Result := FRequestLive; -end; - -procedure TPSQLQuery.SetPrepared(Value: Boolean); -begin - if (FHandle <> nil) and Value then - DatabaseError(SDataSetOpen, Self); - if Value <> Prepared then - begin - if Value then - begin - FRowsAffected := -1; - FCheckRowsAffected := TRUE; - if Length(Text) > 1 then - PrepareSQL(PChar(Text)) else - DatabaseError(SEmptySQLStatement, Self); - end - else - begin - if FCheckRowsAffected then - FRowsAffected := RowsAffected; - end; - FPrepared := Value; - end; -end; - -procedure TPSQLQuery.SetParamsFromCursor; -var - I: Integer; - DataSet: TDataSet; -begin - if FDataLink.DataSource <> nil then - begin - DataSet := FDataLink.DataSource.DataSet; - if DataSet <> nil then - begin - DataSet.FieldDefs.Update; - for I := 0 to FParams.Count - 1 do - if not FParams[I].Bound then - begin - FParams[I].AssignField(DataSet.FieldByName(FParams[I].Name)); - FParams[I].Bound := FALSE; - end; - end; - end; -end; - -procedure TPSQLQuery.RefreshParams; -var - DataSet: TDataSet; -begin - DisableControls; - try - if FDataLink.DataSource <> nil then - begin - DataSet := FDataLink.DataSource.DataSet; - if DataSet <> nil then - if DataSet.Active and (DataSet.State <> dsSetKey) then - begin - TNativeDataset(FHandle).CloseTable; - SetParamsFromCursor(); - if FParams.Count > 0 then - TNativeDataset(FHandle).QuerySetParams(FParams, FSQL.Text); - TNativeDataset(FHandle).OpenTable(); - First(); - end; - end; - finally - EnableControls; - end; -end; - - -function TPSQLQuery.ParamByName(const Value: string): TPSQLParam; -begin - Result := FParams.ParamByName(Value); -end; - -function TPSQLQuery.CreateCursor(GenHandle: Boolean): HDBICur; -begin - if SQL.Count > 0 then - begin - FExecSQL := not GenHandle; - try - SetPrepared(TRUE); - finally - FExecSQL := FALSE; - end; - if FDataLink.DataSource <> nil then SetParamsFromCursor; - Result := GetQueryCursor(GenHandle); - end - else - begin - DatabaseError(SEmptySQLStatement, Self); - Result := nil; - end; - FCheckRowsAffected := (Result = nil); -end; - - -function TPSQLQuery.CreateHandle: HDBICur; -begin - Result := CreateCursor(TRUE) -end; - - -procedure TPSQLQuery.ExecSQL; -begin - CheckInActive; - if Assigned(FBeforeExecSQL) then - FBeforeExecSQL(Self); - SetDBFlag(dbfExecSQL, TRUE); - try - CreateCursor(FALSE); - finally - SetDBFlag(dbfExecSQL, FALSE); - if FHandle <> nil then - begin - Check(Engine, Engine.CloseCursor(hDBICur(FHandle))); - FHandle := nil; - end; - end; -end; - -function TPSQLQuery.GetQueryCursor(GenHandle: Boolean): HDBICur; -const - DataType: array[Boolean] of LongInt = (Ord(wantCanned), Ord(wantLive)); -var - PCursor: phDBICur; - CanLive : boolean; -begin - Result := nil; - if GenHandle then - PCursor := @Result else - PCursor := nil; - if FParams.Count > 0 then - Check(Engine,Engine.QuerySetParams(hDBIStmt(FHandle),Params,SQL.Text)); - Check(Engine, Engine.QExec(hDBIStmt(FHandle), PCursor, FRowsAffected)); - //pasha_golub 20.12.06 - CanLive := False; - if FRequestLive and not ForceUpdateCallback and not FExecSQL then - CanLive := TNativeDataSet(FHandle).CheckCanLive(); - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle), stmtLIVENESS, DataType[CanLive])); - //pasha_golub 20.12.06 -end; - -function TPSQLQuery.SetDBFlag(Flag: Integer; Value: Boolean): Boolean; -var - NewConnection: Boolean; -begin - if Value then - begin - NewConnection := DBFlags = []; - Result := Inherited SetDBFlag(Flag, Value); - if not (csReading in ComponentState) and NewConnection then - FLocal := False; - end - else - begin - if DBFlags - [Flag] = [] then - SetPrepared(FALSE); - Result := Inherited SetDBFlag(Flag, Value); - end; -end; - -procedure TPSQLQuery.SetOptions(const Value: TPSQLDatasetOptions); -begin - if Value = FOptions then Exit; - inherited; - if Active then - begin - Close; - Open; - end; -end; - -procedure TPSQLQuery.PrepareSQL(Value: PChar); -begin - GetStatementHandle(Value); - if not Local then - SetBoolProp(Engine, FHandle, stmtUNIDIRECTIONAL, FUniDirectional); -end; - -procedure TPSQLQuery.GetStatementHandle(SQLText: PChar); -const - DataType: array[Boolean] of LongInt = (Ord(wantCanned), Ord(wantLive)); -var - DBh : DAChDBIDb; -begin - DBh := DBHandle; - Check(Engine,Engine.QAlloc(DBH, hDBIStmt(FHandle))); - try - TNativeDataset(FHandle).Options := Options; - if not FExecSQL then - begin - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),stmtLIVENESS, - DataType[RequestLive and not ForceUpdateCallback])); - end; - if Local then - begin - SetBoolProp(Engine,FHandle,stmtAUXTBLS,FALSE); - SetBoolProp(Engine,FHandle,stmtCANNEDREADONLY,TRUE); - end; - while not CheckOpen(Engine.QPrepare(hDBIStmt(FHandle), SQLText)) do - {Retry}; - except - Engine.QFree(hDBIStmt(FHandle)); - FHandle := nil; - raise; - end; -end; - - -function TPSQLQuery.GetRowsAffected: Integer; -var - Length: integer; -begin - if Prepared then - begin - if Engine.GetEngProp(HDBIObj(FHandle), stmtROWCOUNT, @Result, SizeOf(Result), Length) > DBIERR_NONE then - Result := -1; - end - else - Result := FRowsAffected; -end; - - -{$IFNDEF FPC} -procedure TPSQLQuery.GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF NEXTGEN}{$ENDIF}); - - function AddFieldToList(const FieldName: string; DataSet: TDataSet; - List: TList{$IFDEF NEXTGEN}{$ENDIF}): Boolean; - var - Field: TField; - begin - Field := DataSet.FindField(FieldName); - if (Field <> nil) then - List.Add(Field); - Result := Field <> nil; - end; - -var - i: Integer; -begin - MasterFields.Clear; - DetailFields.Clear; - if (DataSource <> nil) and (DataSource.DataSet <> nil) then - for i := 0 to Params.Count - 1 do - if AddFieldToList(Params[i].Name, DataSource.DataSet, MasterFields) then - AddFieldToList(Params[i].Name, Self, DetailFields); -end; - -{ TPSQLQuery.IProviderSupport } -function TPSQLQuery.PSGetDefaultOrder: TIndexDef; -begin - Result := inherited PSGetDefaultOrder; - if not Assigned(Result) then - Result := GetIndexForOrderBy(SQL.Text, Self); -end; - -function TPSQLQuery.PSGetParams : TParams; -begin - Result := FParams; -end; - -procedure TPSQLQuery.PSSetParams(AParams : TParams); -begin - if (AParams.Count <> 0) then - Params.Assign(AParams); - Close; -end; - -function TPSQLQuery.PSGetTableName: string; -begin - Result := GetTableNameFromSQL(SQL.Text); -end; - -procedure TPSQLQuery.PSExecute; -begin - ExecSQL; -end; - -procedure TPSQLQuery.PSSetCommandText(const CommandText : string); -begin - if (CommandText <> '') then - SQL.Text := CommandText; -end; -{$ENDIF} - -function TPSQLDataSet.GetLastInsertID(const FieldNum: integer): integer; -begin - CheckActive; - if Assigned(FHandle) then - Check(Engine, Engine.GetLastInsertId(FHandle, FieldNum, Result)) - else - Result := -1; -end; - -{$IFDEF DELPHI_12} -function TPSQLDataSet.GetLookupListClass(Field: TField): TLookupListClass; -begin - Result := TPSQLLookupList; -end; -{$ENDIF} - -function TPSQLDataSet.GetStmtHandle: HDBIStmt; -begin - Result := hDBIStmt(GetHandle()); -end; - -function TPSQLDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass; -begin - if (FieldType = ftGuid) and (dsoUseGUIDField in FOptions) then - Result := TPSQLGuidField - else - Result := inherited GetFieldClass(FieldType); -end; - -procedure TPSQLDataSet.SortBy(FieldNames: string); -begin - if Active and (RecordCount > 1) then - try - TNativeDataSet(FHandle).SortBy(FieldNames); - TNativeDataset(Fhandle).SetRowPosition(-1, -1, Pointer(ActiveBuffer)); - Resync([]); - except - FSortFieldNames := ''; - raise; - end; -end; - - -procedure TPSQLDataSet.SortBy(FieldNames: string; Compare: TPSQLDatasetSortCompare); -begin - if Active and (RecordCount > 1) then - begin - TNativeDataSet(FHandle).SortBy(FieldNames, Compare); - TNativeDataset(Fhandle).SetRowPosition(-1, -1, Pointer(ActiveBuffer)); - end; -end; - -procedure TPSQLDataSet.SetOptions(const Value: TPSQLDatasetOptions); -begin - if (dsoFetchOnDemand in Value) then - begin - if not (Self is TPSQLQuery) then DatabaseError('dsoFetchOnDemand option is applicable only to TPSQLQuery objects', Self); - if (Self as TPSQLQuery).RequestLive then DatabaseError('RequestLive must be False to apply dsoFetchOnDemand option', Self); - end; - FOptions := Value; -end; - -procedure TPSQLDataSet.SetSortFieldNames(const Value: string); -begin - if FSortFieldNames <> Value then - begin - SortBy(Value); - FSortFieldNames := Value; - end; -end; - -function TPSQLDataSet.GetSortFieldNames: string; -begin - if not Active or - not Assigned(FHandle) or - not TNativeDataSet(FHandle).IsSortedLocally then - FSortFieldNames := ''; - Result := FSortFieldNames; -end; - -function TPSQLDataSet.GetStoreActive: boolean; -begin - Result := Active - and Assigned(FDatabase) - and ( - (ddoStoreConnected in FDatabase.DesignOptions) - or not (csDesigning in ComponentState) - ); -end; - -function TPSQLDataSet.GetFieldTypeOID(const FieldNum: integer): cardinal; -begin - if Assigned(FHandle) then - Result := Engine.GetFieldTypeOID(FHandle, FieldNum) - else - Result := InvalidOID; -end; - -{ TPSQLUpdateSQL } -constructor TPSQLUpdateSQL.Create(AOwner: TComponent); -var - UpdateKind: TUpdateKind; -begin - Inherited Create(AOwner); - for UpdateKind := Low(TUpdateKind) to High(TUpdateKind) do - begin - FSQLText[UpdateKind] := TStringList.Create; - TStringList(FSQLText[UpdateKind]).OnChange := SQLChanged; - end; -end; - -destructor TPSQLUpdateSQL.Destroy; -var - UpdateKind: TUpdateKind; -begin - if Assigned(FDataSet) and (FDataSet.UpdateObject = Self) then - FDataSet.UpdateObject := nil; - for UpdateKind := Low(TUpdateKind) to High(TUpdateKind) do - FSQLText[UpdateKind].Free; - Inherited Destroy; -end; - -procedure TPSQLUpdateSQL.ExecSQL(UpdateKind: TUpdateKind); -var RN, RC: integer; -begin - with Query[UpdateKind] do - begin - Prepare; - ExecSQL; - if Assigned(FDataSet) then - begin - TNativeDataset(FDataset.Handle).FreeBlobStreams(Pointer(FDataset.ActiveBuffer)); //30.10.2012 - RN := TNativeDataset(FDataset.Handle).RecordNumber; - TNativeDataset(FDataset.Handle).OpenTable; - TNativeDataset(FDataset.Handle).RecordState := tsPos; - if UpdateKind <> ukDelete then - begin - if not TNativeDataset(FDataset.Handle).SetRowPosition(-1, 0, Pointer(FDataset.ActiveBuffer)) then - try - TNativeDataset(FDataset.Handle).SettoSeqNo(RN + 1); - except - end - end - else - begin - if Engine.GetRecordCount(FDataset.Handle, RC) <> DBIERR_NONE then - RC := -1; - if RN >= RC then - RN := 0; - try - TNativeDataset(FDataset.Handle).SettoSeqNo(RN); - except - end; - end; - TNativeDataset(FDataset.Handle).IsLocked := False; - end; - if Assigned(FRecordChangeCompleteEvent) then - FRecordChangeCompleteEvent(FDataset,UpdateKind); - end; -end; - -function TPSQLUpdateSQL.GetQueryClass : TPSQLQueryClass; -begin - Result := TPSQLQuery; -end; - -function TPSQLUpdateSQL.GetQuery(UpdateKind: TUpdateKind): TPSQLQuery; -begin - if not Assigned(FQueries[UpdateKind]) then - begin - FQueries[UpdateKind] := GetQueryClass.Create(Self); - FQueries[UpdateKind].SQL.Assign(FSQLText[UpdateKind]); - if FDataSet is TPSQLDataSet then - FQueries[UpdateKind].Database := TPSQLDataSet(FDataSet).DataBase; - end; - Result := FQueries[UpdateKind]; -end; - -function TPSQLUpdateSQL.GetSQL(UpdateKind: TUpdateKind): TStrings; -begin - Result := FSQLText[UpdateKind]; -end; - -function TPSQLUpdateSQL.GetSQLIndex(Index: Integer): TStrings; -begin - Result := FSQLText[TUpdateKind(Index)]; -end; - -function TPSQLUpdateSQL.GetDataSet: TPSQLDataSet; -begin - Result := FDataSet; -end; - -procedure TPSQLUpdateSQL.SetDataSet(ADataSet: TPSQLDataSet); -begin - FDataSet := ADataSet; -end; - -procedure TPSQLUpdateSQL.SetSQL(UpdateKind: TUpdateKind; Value: TStrings); -begin - FSQLText[UpdateKind].Assign(Value); -end; - -procedure TPSQLUpdateSQL.SetSQLIndex(Index: Integer; Value: TStrings); -begin - SetSQL(TUpdateKind(Index), Value); -end; - -procedure TPSQLUpdateSQL.SQLChanged(Sender: TObject); -var - UpdateKind: TUpdateKind; -begin - for UpdateKind := Low(TUpdateKind) to High(TUpdateKind) do - if Sender = FSQLText[UpdateKind] then - begin - if Assigned(FQueries[UpdateKind]) then - begin - FQueries[UpdateKind].Params.Clear; - FQueries[UpdateKind].SQL.Assign(FSQLText[UpdateKind]); - end; - Break; - end; -end; - -procedure TPSQLUpdateSQL.SetParams(UpdateKind: TUpdateKind); -var - I: Integer; - Old: Boolean; - Param: TPSQLParam; - PName: string; - Field: TField; -begin - if not Assigned(FDataSet) then Exit; - Query[UpdateKind].Database := FDataset.Database; //01.08.2008 - with Query[UpdateKind] do - begin - for I := 0 to Params.Count - 1 do - begin - Param := Params[I]; - PName := Param.Name; - Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; - if Old and (UpdateKind in [ukInsert,ukDelete]) then - DatabaseError(Format(SNoParameterValue,[Param.Name])); - if Old then - System.Delete(PName, 1, 4); - Field := FDataSet.FindField(PName); - if not Assigned(Field) then Continue; - if Old then - Check(FDataset.Engine,FDataset.Engine.GetFieldOldValue(FDataset.Handle, PName, Param)) - else - Check(FDataset.Engine,FDataset.Engine.GetFieldValueFromBuffer(FDataset.Handle, Pointer(FDataset.ActiveBuffer), PName, Param, UpdateKind <> ukModify)); - if Param.DataType = ftUnknown then - Param.DataType := ftString; - end; - end; -end; - -procedure TPSQLUpdateSQL.Apply(UpdateKind: TUpdateKind); -begin - SetParams(UpdateKind); - ExecSQL(UpdateKind); -end; - -/////////////////////////////////////////////////////////////////////////////// -// TPSQLTable // -/////////////////////////////////////////////////////////////////////////////// -constructor TPSQLTable.Create(AOwner: TComponent); -begin - Inherited Create(AOwner); - FIndexDefs := TIndexDefs.Create(Self); - FMasterLink := TMasterDataLink.Create(Self); - FMasterLink.OnMasterChange := MasterChanged; - FMasterLink.OnMasterDisable := MasterDisabled; - FDefaultIndex := TRUE; - CacheBlobs := False; - FLimit := 0; -end; - -destructor TPSQLTable.Destroy; -begin - Inherited Destroy; - FMasterLink.Free; - FIndexDefs.Free; -end; - -function TPSQLTable.GetLimit: Integer; -begin - Result := FLimit; -end; - -procedure TPSQLTable.SetLimit(const Value : Integer); -begin - if FLimit <> Value then - FLimit := Value; -end; - -function TPSQLTable.GetHandle(const IndexName, IndexTag: string): HDBICur; -const - OpenModes: array[Boolean] of DbiOpenMode = (dbiReadWrite, dbiReadOnly); - ShareModes: array[Boolean] of DbiShareMode = (dbiOpenShared, dbiOpenExcl); -var - IndexID: Word; - OpenMode: DbiOpenMode; - RetCode: Word; - DBH : DAChDBIDb; - - procedure FillAddonProps; - begin - Check(Engine,Engine.GetTableProps(DBHandle, FTableName, FOwner, - FComment, FTablespace, FTableID)); - end; - -begin - Result := nil; - OpenMode := OpenModes[FReadOnly]; - if DefaultIndex then - IndexID := 0 - else - IndexID := NODEFAULTINDEX; - while TRUE do - begin - DBH := DBHandle; - RetCode := Engine.OpenTable(DBH, FTableName, IndexName, IndexID, OpenMode, ShareModes[FExclusive], - Result, FOptions, FLimit, FOffset); - if RetCode = DBIERR_TABLEREADONLY then - OpenMode := dbiReadOnly - else - FillAddonProps(); - if CheckOpen(RetCode) then Break; - end; -end; - -function TPSQLTable.Engine : TPSQLEngine; -begin - Result := FDataBase.Engine; -end; - -function TPSQLTable.CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; -begin - Result := TPSQLBlobStream.Create(Field as TBlobField, Mode); -end; - -function TPSQLTable.IsSequenced: Boolean; -begin - Result := FAllowSequenced and inherited IsSequenced; -end; - -function TPSQLTable.CreateHandle: HDBICur; -var - AnIndexName, IndexTag: string; -begin - if FTableName = '' then DatabaseError(SNoTableName, Self); - IndexDefs.Updated := FALSE; - GetIndexParams(FIndexName, FFieldsIndex, AnIndexName, IndexTag); - Result := GetHandle(AnIndexName, IndexTag); - TNativeDataset(Result).Options := Options; -end; - -function TPSQLTable.GetLanguageDriverName: string; -begin - Result := ''; -end; - -procedure TPSQLTable.PrepareCursor; -begin - CheckMasterRange; -end; - -{$IFNDEF FPC} -procedure TPSQLTable.DefChanged(Sender: TObject); -begin - StoreDefs := TRUE; -end; -{$ENDIF} - -procedure TPSQLTable.InitFieldDefs; -var - I, FieldID, FldDescCount: Integer; - FieldDescs: TFLDDescList; - FCursor: HDBICur; - RequiredFields: TBits; -begin - if FHandle <> nil then - InternalInitFieldDefs else - begin - SetDBFlag(dbfFieldList, TRUE); - try - if (FTableName = '') then DatabaseError(SNoTableName, Self); - while not CheckOpen(Engine.OpenFieldList(DBHandle, FTableName, - '', FALSE, FCursor)) do {Retry}; - try - Check(Engine, Engine.GetRecordCount(FCursor, FldDescCount)); - SetLength(FieldDescs, FldDescCount); - { Create an array of field descriptors } - for I := 0 to FldDescCount - 1 do - Check(Engine, Engine.GetNextRecord(FCursor, dbiNoLock, @FieldDescs[I], nil)); - { Initialize list of required fields } - RequiredFields := TBits.Create; - try - if FieldDescs[FldDescCount-1].iFldNum > FldDescCount then - RequiredFields.Size := FieldDescs[FldDescCount-1].iFldNum + 1 - else - RequiredFields.Size := FldDescCount + 1; - { Initialize the FieldDefs } - FieldDefs.BeginUpdate; - try - FieldDefs.Clear; - I := 0; - FieldID := 1; - while I < FldDescCount do - AddFieldDesc(FieldDescs, I, FieldID, RequiredFields, FieldDefs); - finally - FieldDefs.EndUpdate; - end; - finally - RequiredFields.Free; - end; - finally - Engine.CloseCursor(FCursor); - end; - finally - SetDBFlag(dbfFieldList, FALSE); - end; - end; -end; - -procedure TPSQLTable.DestroyHandle; -begin - inherited DestroyHandle; -end; - -procedure TPSQLTable.DecodeIndexDesc(const IndexDesc: IDXDesc; - var Source, Name, FieldExpression, DescFields: string; - var Options: TIndexOptions); - - procedure ConcatField(var FieldList: string; const FieldName: string); - begin - if FieldList = '' then - FieldList := FieldName else - FieldList := Format('%s;%s', [FieldList, FieldName]); - end; - -var - IndexOptions: TIndexOptions; - I: Integer; - FieldName: string; - s : string; -begin - with IndexDesc do - begin - S := szName; - Name := ExtractFileName(string(s)); - Source := ExtractFileName(Source); - IndexOptions := []; - if bPrimary then Include(IndexOptions, ixPrimary); - if bUnique then Include(IndexOptions, ixUnique); - if bDescending then Include(IndexOptions, ixDescending); - if bCaseInsensitive then Include(IndexOptions, ixCaseInsensitive); - if not bMaintained then Include(IndexOptions, ixNonMaintained); - if bExpIdx then - begin - //TNativeToAnsi(Engine, szKeyExp, S); - S := szKeyExp; - FieldExpression := string(s); - Include(IndexOptions, ixExpression); - end else - begin - FieldExpression := ''; - DescFields := ''; - for I := 0 to iFldsInKey - 1 do - begin - FieldName := FieldDefs[aiKeyFld[I] - 1].Name; - ConcatField(FieldExpression, FieldName); - if abDescending[I] then - ConcatField(DescFields, FieldName); - end; - if bDescending and (DescFields = FieldExpression) then DescFields := ''; - end; - Options := IndexOptions; - end; -end; - -procedure TPSQLTable.EncodeIndexDesc(var IndexDesc: IDXDesc; - const Name, FieldExpression: string; Options: TIndexOptions; - const DescFields: string); - - function IndexFieldOfs(const FieldName: string): Integer; - var - FieldNo: Integer; - begin - FieldNo := FieldDefs.Find(FieldName).FieldNo; - for Result := 0 to IndexDesc.iFldsInKey - 1 do - if IndexDesc.aiKeyFld[Result] = FieldNo then - Exit; - DatabaseErrorFmt(SIndexFieldMissing, [FieldName], Self); - Result := -1; - end; - -var - Pos: Integer; -begin - FillChar(IndexDesc, SizeOf(IndexDesc), 0); - with IndexDesc do - begin - //Move(Name[{$IFNDEF NEXTGEN}1{$ELSE}0{$ENDIF}], szName, Max(Length(Name), DBIMAXNAMELEN) * SizeOf(Char)); - IndexDesc.szName := Name; - bPrimary := ixPrimary in Options; - bUnique := ixUnique in Options; - bDescending := (ixDescending in Options) and (DescFields = ''); - bMaintained := not (ixNonMaintained in Options); - Word(bCaseInsensitive) := Word(ixCaseInsensitive in Options); - if ixExpression in Options then - begin - bExpIdx := TRUE; - //TAnsiToNative(Engine, FieldExpression, szKeyExp, SizeOf(szKeyExp) - 1); - szKeyExp := FieldExpression; - end - else - begin - Pos := 1; - while (Pos <= Length(FieldExpression)) and (iFldsInKey < DBIMAXFLDSINKEY) do - begin - aiKeyFld[iFldsInKey] := - FieldDefs.Find(ExtractFieldName(FieldExpression, Pos)).FieldNo; - Inc(iFldsInKey); - end; - if (DescFields <> '') then - begin - bDescending := TRUE; - Pos := 1; - while Pos <= Length(DescFields) do - abDescending[IndexFieldOfs(ExtractFieldName(DescFields, Pos))] := TRUE; - end; - end; - end; -end; - -procedure TPSQLTable.AddIndex(const Name, Fields: string; Options: TIndexOptions; - const DescFields: string); -var - IndexDesc: IDXDesc; -begin - FieldDefs.Update; - if Active then - begin - EncodeIndexDesc(IndexDesc, Name, Fields, Options, DescFields); - CheckBrowseMode; - CursorPosChanged; - Check(Engine, Engine.AddIndex(DBHandle, Handle, '', '', IndexDesc, '')); - end - else - begin - EncodeIndexDesc(IndexDesc, Name, Fields, Options, DescFields); - SetDBFlag(dbfTable, TRUE); - try - Check(Engine, Engine.AddIndex(DBHandle, nil, FTableName, '', IndexDesc, '')); - finally - SetDBFlag(dbfTable, FALSE); - end; - end; - IndexDefs.Updated := FALSE; -end; - -procedure TPSQLTable.DeleteIndex(const Name: string); -var - AnIndexName, IndexTag: string; -begin - if Active then - begin - GetIndexParams(Name, FALSE, AnIndexName, IndexTag); - CheckBrowseMode; - Check(Engine, Engine.DeleteIndex(DBHandle, Handle, '', '', AnIndexName, IndexTag, 0)); - end - else - begin - GetIndexParams(Name, FALSE, AnIndexName, IndexTag); - SetDBFlag(dbfTable, TRUE); - try - Check(Engine, Engine.DeleteIndex(DBHandle, nil, FTableName, '', - AnIndexName, IndexTag, 0)); - finally - SetDBFlag(dbfTable, FALSE); - end; - end; - FIndexDefs.Updated := FALSE; -end; - -function TPSQLTable.GetIndexFieldNames: string; -begin - if FFieldsIndex then Result := FIndexName else Result := ''; -end; - -function TPSQLTable.GetIndexName: string; -begin - if FFieldsIndex then Result := '' else Result := FIndexName; -end; - -procedure TPSQLTable.GetIndexNames(List: TStrings); -begin - IndexDefs.Update; - IndexDefs.GetItemNames(List); -end; - -procedure TPSQLTable.GetIndexParams(const IndexName: string; - FieldsIndex: Boolean; var IndexedName, IndexTag: string); -var - IndexStr: TIndexName; -begin - IndexStr := ''; - if IndexName <> '' then - begin - IndexDefs.Update; - IndexStr := IndexName; - if FieldsIndex then - IndexStr := IndexDefs.FindIndexForFields(IndexName).Name; - end; - IndexedName := IndexStr; - IndexTag := ''; -end; - -procedure TPSQLTable.SetIndexDefs(Value: TIndexDefs); -begin - IndexDefs.Assign(Value); -end; - -procedure TPSQLTable.SetIndex(const Value: string; FieldsIndex: Boolean); -var - AnIndexName, IndexTag: string; -begin - if Active then CheckBrowseMode; - if (FIndexName <> Value) or (FFieldsIndex <> FieldsIndex) then - begin - if Active then - begin - GetIndexParams(Value, FieldsIndex, AnIndexName, IndexTag); - SwitchToIndex(AnIndexName, IndexTag); - CheckMasterRange; - end; - FIndexName := Value; - FFieldsIndex := FieldsIndex; - if Active then Resync([]); - end; -end; - -procedure TPSQLTable.SetIndexFieldNames(const Value: string); -begin - SetIndex(Value, Value <> ''); -end; - -procedure TPSQLTable.SetIndexName(const Value: string); -begin - SetIndex(Value, FALSE); -end; - -procedure TPSQLTable.UpdateIndexDefs; -var - Opts: TIndexOptions; - IdxName, Src, Flds, DescFlds: string; - - procedure UpdateFromCursor; - var - I: Integer; - Cursor: HDBICur; - CursorProps: CurProps; - IndexDescs: TIDXDescList; - begin - if Handle = nil then - Cursor := GetHandle('', '') else - Cursor := Handle; - try - Engine.GetCursorProps(Cursor, CursorProps); - if CursorProps.iIndexes > 0 then - begin - SetLength(IndexDescs, CursorProps.iIndexes); - Engine.GetIndexDescs(Cursor, IndexDescs); - for I := 0 to CursorProps.iIndexes - 1 do - begin - DecodeIndexDesc(IndexDescs[I], Src, IdxName, Flds, DescFlds, Opts); - with IndexDefs.AddIndexDef do - begin - Name := IdxName; - Fields := Flds; - DescFields := DescFlds; - Options := Opts; - if Src <> '' then Source := Src; - end; - end; - end; - finally - if (Cursor <> nil) and (Cursor <> Handle) then Engine.CloseCursor(Cursor); - end; - end; - - procedure UpdateFromIndexList; - var - FCursor: HDBICur; - IndexDesc: IDXDesc; - begin - while not CheckOpen(Engine.OpenIndexList(DBHandle, FTableName, '', FCursor)) do {Retry}; - try - while Engine.GetNextRecord(FCursor, dbiNoLock, @IndexDesc, nil) = 0 do - if IndexDesc.bMaintained then - begin - DecodeIndexDesc(IndexDesc, Src, IdxName, Flds, DescFlds, Opts); - with IndexDefs.AddIndexDef do - begin - Name := IdxName; - Fields := Flds; - DescFields := DescFlds; - Options := Opts; - end; - Finalize(IndexDesc); - end; - finally - Engine.CloseCursor(FCursor); - end; - end; - -begin - Inc(FDatabase.FRefCount); - SetDBFlag(dbfIndexList, TRUE); - try - FieldDefs.Update; - IndexDefs.Clear; - if IsCursorOpen then - UpdateFromCursor else - UpdateFromIndexList; - finally - SetDBFlag(dbfIndexList, FALSE); - end; -end; - -function TPSQLTable.GetExists: Boolean; -begin - Result := Active; - if Result or (TableName = '') or not Assigned(Database) then Exit; - SetDBFlag(dbfTable, TRUE); - try - Database.SelectString('SELECT ' + QuotedStr(TableName) + ' :: regclass', Result) - finally - SetDBFlag(dbfTable, FALSE); - end; -end; - -function TPSQLTable.FindKey(const KeyValues: array of const): Boolean; -begin - CheckBrowseMode; - SetKeyFields(kiLookup, KeyValues); - Result := GotoKey; -end; - -procedure TPSQLTable.FindNearest(const KeyValues: array of const); -begin - CheckBrowseMode; - SetKeyFields(kiLookup, KeyValues); - GotoNearest; -end; - -{$HINTS OFF} -function TPSQLTable.GotoKey: Boolean; -var - KeyBuffer: PKeyBuffer; - IndexBuffer, RecBuffer: PAnsiDACChar; - UseKey: Boolean; -begin - CheckBrowseMode; - DoBeforeScroll; - CursorPosChanged; - KeyBuffer := GetKeyBuffer(kiLookup); - IndexBuffer := AllocMem(KeySize); - try - RecBuffer := PAnsiDACChar(KeyBuffer) + SizeOf(TKeyBuffer); - UseKey := Engine.ExtractKey(Handle, RecBuffer, IndexBuffer) = 0; - if UseKey then RecBuffer := IndexBuffer; - Result := Engine.GetRecordForKey(Handle, UseKey, KeyBuffer^.FieldCount, 0, RecBuffer, nil,True) = 0; - if Result then Resync([rmExact, rmCenter]); - if Result then DoAfterScroll; - finally - FreeMem(IndexBuffer, KeySize); - end; -end; - -procedure TPSQLTable.GotoNearest; -var - SearchCond: DBISearchCond; - KeyBuffer: PKeyBuffer; - IndexBuffer, RecBuffer: PAnsiDACChar; - UseKey: Boolean; -begin - CheckBrowseMode; - CursorPosChanged; - KeyBuffer := GetKeyBuffer(kiLookup); - if KeyBuffer^.Exclusive then - SearchCond := keySEARCHGT else - SearchCond := keySEARCHGEQ; - IndexBuffer := AllocMem(KeySize); - try - RecBuffer := PAnsiDACChar(KeyBuffer) + SizeOf(TKeyBuffer); - UseKey := Engine.ExtractKey(Handle,RecBuffer,IndexBuffer) = 0; - if UseKey then RecBuffer := IndexBuffer; - - if Engine.GetRecordForKey(Handle, {SearchCond,} UseKey, KeyBuffer^.FieldCount, 0,RecBuffer, nil, False) = 0 - then Resync([rmCenter]); - finally - FreeMem(IndexBuffer, KeySize); - end; -end; -{$HINTS ON} - -procedure TPSQLTable.SetKey; -begin - SetKeyBuffer(kiLookup, TRUE); -end; - -procedure TPSQLTable.EditKey; -begin - SetKeyBuffer(kiLookup, FALSE); -end; - -procedure TPSQLTable.ApplyRange; -begin - CheckBrowseMode; - if SetCursorRange then First; -end; - -procedure TPSQLTable.CancelRange; -begin - CheckBrowseMode; - UpdateCursorPos; - if ResetCursorRange then Resync([]); -end; - -procedure TPSQLTable.SetRange(const StartValues, EndValues: array of const); -begin - CheckBrowseMode; - SetKeyFields(kiRangeStart, StartValues); - SetKeyFields(kiRangeEnd, EndValues); - ApplyRange; -end; - -procedure TPSQLTable.SetRangeEnd; -begin - SetKeyBuffer(kiRangeEnd, TRUE); -end; - -procedure TPSQLTable.SetRangeStart; -begin - SetKeyBuffer(kiRangeStart, TRUE); -end; - -procedure TPSQLTable.EditRangeEnd; -begin - SetKeyBuffer(kiRangeEnd, FALSE); -end; - -procedure TPSQLTable.EditRangeStart; -begin - SetKeyBuffer(kiRangeStart, FALSE); -end; - -procedure TPSQLTable.UpdateRange; -begin - SetLinkRanges(FMasterLink.Fields); -end; - -function TPSQLTable.GetBatchModify: Boolean; -var - Len : integer; -begin - if Assigned(FHandle) then - Engine.GetEngProp(hDBIObj(FHandle), curAUTOREFETCH, @Result, SizeOf(Result), Len) - else - Result := False; -end; - -procedure TPSQLTable.SetBatchModify(const Value : Boolean); -begin - if FHandle = nil then Exit; - if Value then - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),curAUTOREFETCH,LongInt(TRUE))) else - begin - Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),curAUTOREFETCH,LongInt(FALSE))); - Refresh; - end; -end; - -procedure TPSQLTable.GotoCurrent(Table: TPSQLTable); -begin - CheckBrowseMode; - Table.CheckBrowseMode; - if (AnsiCompareText(FDatabase.DatabaseName, Table.Database.DatabaseName) <> 0) or - (AnsiCompareText(TableName, Table.TableName) <> 0) then DatabaseError(STableMismatch, Self); - Table.UpdateCursorPos; - DoBeforeScroll; - Resync([rmExact, rmCenter]); - DoAfterScroll; -end; - -{$IFNDEF FPC} -procedure TPSQLTable.GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF DELPHI_17}{$ENDIF}); -var - i: Integer; - Idx: TIndexDef; -begin - MasterFields.Clear; - DetailFields.Clear; - if (MasterSource <> nil) and (MasterSource.DataSet <> nil) and (Self.MasterFields <> '') then - begin - Idx := nil; - MasterSource.DataSet.GetFieldList(MasterFields, Self.MasterFields); - UpdateIndexDefs; - if IndexName <> '' then - Idx := IndexDefs.Find(IndexName) - else - if IndexFieldNames <> '' then - Idx := IndexDefs.GetIndexForFields(IndexFieldNames, FALSE) - else - for i := 0 to IndexDefs.Count - 1 do - if ixPrimary in IndexDefs[i].Options then - begin - Idx := IndexDefs[i]; - break; - end; - if Idx <> nil then GetFieldList(DetailFields, Idx.Fields); - end; -end; -{$ENDIF} - -procedure TPSQLTable.CheckMasterRange; -begin - if FMasterLink.Active and (FMasterLink.Fields.Count > 0) then - begin - SetLinkRanges(FMasterLink.Fields); - SetCursorRange; - end; -end; - -procedure TPSQLTable.MasterChanged(Sender: TObject); -begin - CheckBrowseMode; - UpdateRange; - ApplyRange; -end; - -procedure TPSQLTable.MasterDisabled(Sender: TObject); -begin - CancelRange; -end; - -function TPSQLTable.GetDataSource: TDataSource; -begin - Result := FMasterLink.DataSource; -end; - -procedure TPSQLTable.SetDataSource(Value: TDataSource); -begin - if IsLinkedTo(Value) then - DatabaseError(SCircularDataLink, Self); - FMasterLink.DataSource := Value; -end; - -function TPSQLTable.GetMasterFields: string; -begin - Result := FMasterLink.FieldNames; -end; - -procedure TPSQLTable.SetMasterFields(const Value: string); -begin - FMasterLink.FieldNames := Value; -end; - -procedure TPSQLTable.DoOnNewRecord; -var - I: Integer; -begin - if FMasterLink.Active and (FMasterLink.Fields.Count > 0) then - for I := 0 to Pred(FMasterLink.Fields.Count) do - IndexFields[I] := TField(FMasterLink.Fields[I]); - Inherited DoOnNewRecord; -end; - -// pg: 01.03.2011 -procedure TPSQLTable.CreateTable; - - function CreateSQLForCreateTable:string; - var j : Integer; - begin - Result := Format('CREATE TABLE %s ( ',[TableName]); - for j := 0 to FieldDefs.Count - 1 do - begin - Result := Result + BDETOPSQLStr(FieldDefs[j]); - if j < FieldDefs.Count - 1 then - Result := Result + ', ' - else - Result := Result + '); '; - end; - end; - -var - i: integer; -begin - CheckInactive; - SetDBFlag(dbfTable, True); - try - Check(Engine,Engine.QExecDirect(DBHandle, CreateSQLForCreateTable, nil, I)); - //indexes - for I := 0 to IndexDefs.Count - 1 do - AddIndex(IndexDefs[I].Name, IndexDefs[I].Fields, IndexDefs[i].Options); - finally - SetDBFlag(dbfTable, False); - end; -end; - -procedure TPSQLTable.EmptyTable; -begin - if Active then - begin - CheckBrowseMode; - Check(Engine, Engine.EmptyTable(DBHandle, Handle, '', '')); - ClearBuffers; - DataEvent(deDataSetChange, 0); - end else - begin - SetDBFlag(dbfTable, TRUE); - try - Check(Engine, Engine.EmptyTable(DBHandle, nil, FTableName, '')); - finally - SetDBFlag(dbfTable, FALSE); - end; - end; -end; - -procedure TPSQLTable.LockTable(LockType: TPSQLLockType; NoWait: boolean); -begin - CheckActive; - if not Database.InTransaction then - DatabaseError('LOCK TABLE can not be used outside the transaction.'); - Check(Engine, Engine.AcqTableLock(Handle, Word(LockType), NoWait)); -end; - -procedure TPSQLTable.EncodeFieldDesc(var FieldDesc: FLDDesc; - const Name: string; DataType: TFieldType; Size, Precision: Integer); -begin - with FieldDesc do - begin - //TAnsiToNative(Engine, Name, szName, SizeOf(szName) - 1); - szName := Name; - iFldType := FldTypeMap[DataType]; - iSubType := FldSubTypeMap[DataType]; - case DataType of - ftString, ftFixedChar, ftBytes, ftVarBytes, ftBlob..ftTypedBinary: - iUnits1 := Size; - ftBCD: - begin - { Default precision is 32, Size = Scale } - if (Precision > 0) and (Precision <= 32) then - iUnits1 := Precision - else - iUnits1 := 32; - iUnits2 := Size; - end; - end; - end; -end; - -procedure TPSQLTable.DataEvent(Event: TDataEvent; Info: TDataEventInfo); -begin - if Event = depropertyChange then - IndexDefs.Updated := FALSE; - Inherited DataEvent(Event, Info); -end; - -function TPSQLTable.GetCanModify: Boolean; -begin - Result := Inherited GetCanModify and not ReadOnly; -end; - -function TPSQLTable.GetTableLevel: Integer; -begin - if Handle <> nil then - Result := GetIntProp(Engine, Handle, curTABLELEVEL) else - Result := FTableLevel; -end; - -function TPSQLTable.FieldDefsStored: Boolean; -begin - Result := StoreDefs and (FieldDefs.Count > 0); -end; - -function TPSQLTable.IndexDefsStored: Boolean; -begin - Result := StoreDefs and (IndexDefs.Count > 0); -end; - -function TPSQLTable.GetFileName: string; -var - FDb: Boolean; -begin - FDb := SetDBFlag(dbfDatabase, TRUE); - try - Result := Result + TableName; - finally - SetDBFlag(dbfDatabase, FDb); - end; -end; - -function TPSQLTable.GetTableType: TTableType; -begin - Result := ttDefault; -end; - -function TPSQLTable.NativeTableName: PAnsiDACChar; -begin -{$IFNDEF NEXTGEN} - Result := PAnsiChar(AnsiString(FTableName)); -{$ELSE} - Result := PAnsiDACChar(Pointer(FTableName)); -{$ENDIF} -end; - -procedure TPSQLTable.SetExclusive(Value: Boolean); -begin - CheckInactive; - FExclusive := Value; -end; - -procedure TPSQLTable.SetReadOnly(Value: Boolean); -begin - CheckInactive; - FReadOnly := Value; -end; - -procedure TPSQLTable.SetTableName(const Value: TFileName); -begin - if csReading in ComponentState then - FTableName := Value - else - if FTableName <> Value then - begin - CheckInactive; - //changed by pasha_golub 23.12.04 - FTableName := Value; - FNativeTableName[0] := #0; - DataEvent(dePropertyChange, 0); - end; -end; - -function TPSQLTable.GetTableName: TFileName; -begin - Result := FTableName; -end; - -{ TTable.IProviderSupport } -{$IFNDEF FPC} -function TPSQLTable.PSGetDefaultOrder: TIndexDef; - - function GetIdx(IdxType : TIndexOption) : TIndexDef; - var - i: Integer; - L: TList{$IFDEF DELPHI_17}{$ENDIF}; - begin - Result := nil; - L := nil; - for i := 0 to IndexDefs.Count - 1 do - if IdxType in IndexDefs[i].Options then - try - Result := IndexDefs[ i ]; - GetFieldList(L, Result.Fields); - break; - except - Result := nil; - end; - end; - -var - DefIdx: TIndexDef; - L: TList{$IFDEF DELPHI_17}{$ENDIF}; -begin - DefIdx := nil; - L := nil; - IndexDefs.Update; - try - if (IndexName <> '') then - DefIdx := IndexDefs.Find(IndexName) - else - if (IndexFieldNames <> '') then - DefIdx := IndexDefs.FindIndexForFields(IndexFieldNames); - if Assigned(DefIdx) then - GetFieldList(L, DefIdx.Fields); - except - DefIdx := nil; - end; - if not Assigned(DefIdx) then - DefIdx := GetIdx(ixPrimary); - if not Assigned(DefIdx) then - DefIdx := GetIdx(ixUnique); - if Assigned(DefIdx) then - begin - Result := TIndexDef.Create(nil); - Result.Assign(DefIdx); - end - else - Result := nil; -end; - -function TPSQLTable.PSGetIndexDefs(IndexTypes : TIndexOptions): TIndexDefs; -begin - Result := GetIndexDefs(IndexDefs, IndexTypes); -end; - -function TPSQLTable.PSGetTableName: string; -begin - Result := TableName; -end; - -procedure TPSQLTable.PSSetParams(AParams: TParams); - - procedure AssignFields; - var - I: Integer; - begin - for I := 0 to AParams.Count - 1 do - if (AParams[ I ].Name <> '') then - FieldByName(AParams[ I ].Name).Value := AParams[ I ].Value - else - IndexFields[ I ].Value := AParams[ I ].Value; - end; - -begin - if (AParams.Count > 0) then - begin - Open; - SetRangeStart; - AssignFields; - SetRangeEnd; - AssignFields; - ApplyRange; - end - else - if Active then - CancelRange; - PSReset; -end; - -procedure TPSQLTable.PSSetCommandText(const CommandText : string); -begin - if CommandText <> '' then - TableName := CommandText; -end; - -function TPSQLTable.PSGetKeyFields: string; -var - i, Pos: Integer; - IndexFound: Boolean; -begin - Result := inherited PSGetKeyFields; - if Result = '' then - begin - if not Exists then Exit; - IndexFound := FALSE; - IndexDefs.Update; - for i := 0 to IndexDefs.Count - 1 do - if ixUnique in IndexDefs[I].Options then - begin - Result := IndexDefs[ I ].Fields; - IndexFound := (FieldCount = 0); - if not IndexFound then - begin - Pos := 1; - while (Pos <= Length(Result)) do - begin - IndexFound := (FindField(ExtractFieldName(Result, Pos)) <> nil); - if not IndexFound then - Break; - end; - end; - if IndexFound then Break; - end; - if not IndexFound then Result := ''; - end; -end; -{$ENDIF} - -/////////////////////////////////////////////////////////////////////////////// -// TPSQLBlobStream // -/////////////////////////////////////////////////////////////////////////////// -constructor TPSQLBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode); -var - OpenMode: DbiOpenMode; -begin - inherited Create; - FMode := Mode; - FField := Field; - FDataSet := FField.DataSet as TPSQLDataSet; - FFieldNo := FField.FieldNo; - - if not FDataSet.GetActiveRecBuf(FBuffer) then Exit; - - if FDataSet.State = dsFilter then - DatabaseErrorFmt(SNoFieldAccess, [FField.DisplayName], FDataSet); - - if not FField.Modified then - begin - if Mode = bmRead then - begin - FCached := FDataSet.FCacheBlobs and (FBuffer = TRecordBuffer(FDataSet.ActiveBuffer)) and - (FField.IsNull or (FDataSet.GetBlobData(FField, FBuffer) <> {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF})); - OpenMode := dbiReadOnly; - end - else - begin //bmWrite - FDataSet.SetBlobData(FField, FBuffer, {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}); - if FField.ReadOnly then DatabaseErrorFmt(SFieldReadOnly, [FField.DisplayName], FDataSet); - if not (FDataSet.State in [dsEdit, dsInsert]) then DatabaseError(SNotEditing, FDataSet); - OpenMode := dbiReadWrite; - end; - - if not FCached then - begin - if Mode = bmRead then - begin - if FDataSet.State = dsBrowse then - begin - FDataSet.GetCurrentRecord(FDataSet.ActiveBuffer); - end - else if (FDataSet.State = dsEdit) or (FDataSet.State = dsInsert) then - begin - TNativeDataSet(FDataSet.FHandle).PreventRememberBuffer := true; //we just need to read the record without storing in recordbuffer - FDataSet.GetCurrentRecord(FDataSet.ActiveBuffer); - TNativeDataSet(FDataSet.FHandle).PreventRememberBuffer := false; - end; - end; - - Check(Engine, Engine.OpenBlob(FDataSet.Handle, FBuffer, FFieldNo, OpenMode)); - end; - - end; - - FOpened := TRUE; - - if Mode = bmWrite then Truncate; -end; - -destructor TPSQLBlobStream.Destroy; -begin - if FOpened then - begin - if FModified then FField.Modified := TRUE; - if not FField.Modified and not FCached then Engine.FreeBlob(FDataSet.Handle, FBuffer, FFieldNo); - Engine.CloseBlob(FDataset.Handle, FFieldNo); //17.08.2009 - end; - if FModified then - try - FDataSet.DataEvent(deFieldChange, TDataEventInfo(FField)); - except - {$IFNDEF FPC} - Self.FDataSet.InternalHandleException(); - {$ENDIF} - end; - inherited; -end; - -function TPSQLBlobStream.Engine : TPSQLEngine; -begin - Result := FDataSet.Engine; -end; - -function TPSQLBlobStream.PositionDataset: Boolean; -begin - Result := True; -end; - -{$IFDEF DELPHI_17} -function TPSQLBlobStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint; -begin - Result := Read(Buffer[0], Count); -end; -{$ENDIF DELPHI_17} - -function TPSQLBlobStream.Read(var Buffer; Count: Longint): Longint; -var - Status: DBIResult; - Res: integer; -begin - Result := 0; - //P := @Buffer; - if FOpened then - begin - if FCached then - begin - if Count > Size - FPosition then - Result := Size - FPosition else - Result := Count; - if Result > 0 then - begin - Move(PAnsiDACChar(FDataSet.GetBlobData(FField, FBuffer))[FPosition], Buffer, Res); - Result := Res; //compiler wants implicit type casting for NEXTGEN - Inc(FPosition, Result); - end; - end else - begin - Status := Engine.GetBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition, Count, @Buffer, Res); - Result := Res; //compiler wants implicit type casting for NEXTGEN - case Status of - DBIERR_NONE, DBIERR_ENDOFBLOB: - begin - {if FField.Transliterate then - TNativeToAnsiBuf(Engine, @Buffer, @Buffer, Result);} - if FDataset.FCacheBlobs and (FBuffer = TRecordBuffer(FDataSet.ActiveBuffer)) and - (FMode = bmRead) and not FField.Modified and (FPosition = FCacheSize) then - begin - FCacheSize := FPosition + Result; - SetLength(FBlobData, FCacheSize); - Move(Buffer, PAnsiDACChar(FBlobData)[FPosition], Result); - if FCacheSize = Size then - begin - FDataSet.SetBlobData(FField, FBuffer, FBlobData); - FBlobData := {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}; - FCached := TRUE; - Engine.FreeBlob(FDataSet.Handle, FBuffer, FFieldNo); - end; - end; - Inc(FPosition, Result); - end; - DBIERR_INVALIDBLOBOFFSET: - {Nothing}; - else - TDbiError(Engine, Status); - end; - end; - end; -end; - -function TPSQLBlobStream.Write(const Buffer; Count: Longint): Longint; -var - Temp, P: Pointer; -begin - Result := 0; - P := @Buffer; - FField.Transliterate := false; - if FOpened then - begin - if FField.Transliterate then - begin - GetMem(Temp, Count+1); - try - //TAnsiToNativeBuf(Engine, @Buffer, Temp, Count); - Check(Engine, Engine.PutBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition, Count, @Buffer)); - finally - FreeMem(Temp, Count+1); - end; - end else - Check(Engine, Engine.PutBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition, Count, P)); - Inc(FPosition, Count); - Result := Count; - FModified := TRUE; - FDataSet.SetBlobData(FField, FBuffer, {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}); - end; -end; - - -function TPSQLBlobStream.Seek(Offset: Longint; Origin: Word): Longint; -begin - case Origin of - 0: FPosition := Offset; - 1: Inc(FPosition, Offset); - 2: FPosition := GetBlobSize + Offset; - end; - Result := FPosition; -end; - -procedure TPSQLBlobStream.Truncate; -begin - if FOpened then - begin - Check(Engine, Engine.TruncateBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition)); - FModified := TRUE; - FDataSet.SetBlobData(FField, FBuffer, {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}); - end; -end; - -function TPSQLBlobStream.GetBlobSize: integer; -begin - Result := 0; - if FOpened then - if FCached then - Result := Length(FDataSet.GetBlobData(FField, FBuffer)) else - Check(Engine, Engine.GetBlobSize(FDataSet.Handle, FBuffer, FFieldNo, Result)); -end; - -{ TPSQLQueryDataLink } -constructor TPSQLQueryDataLink.Create(AQuery : TPSQLQuery); -begin - Inherited Create; - FQuery := AQuery; -end; - -procedure TPSQLQueryDataLink.ActiveChanged; -begin - if FQuery.Active then FQuery.RefreshParams; -end; - -function TPSQLQueryDataLink.GetDetailDataSet: TDataSet; -begin - Result := FQuery; -end; - -procedure TPSQLQueryDataLink.RecordChanged(Field : TField); -begin - if (Field = nil)and FQuery.Active then FQuery.RefreshParams; -end; - -procedure TPSQLQueryDataLink.CheckBrowseMode; -begin - if FQuery.Active then FQuery.CheckBrowseMode; -end; - -var - SaveInitProc: Pointer; - -procedure InitDBTables; -begin - if (SaveInitProc <> nil) then - TProcedure(SaveInitProc); -end; - - -procedure TPSQLTable.SetShowSystemTables(const Value: boolean); -begin - if FShowSystemTables <> Value then - begin - FShowSystemTables := Value; - IF not Value and - ((pos('pg_',FTableName) = 1) or - (pos('information_schema',FTableName)=1)) then - TableName := ''; - end; -end; - - - -{ TPSQLStoredProc } -const - SEmptyprocedureName = 'procedure name is empty'; - SDatabaseProperty = '(%s) property Database is not set!'; - SCantCreateWriteBLOB = 'Can''t create BLOB stream with write permissions on read-only result set!'; - -procedure TPSQLStoredProc.CloseCursor; -var - r : DbiResult; -begin - inherited; - - if FHandle <> nil then - begin - r := Engine.CloseCursor(FHandle); - FHandle := nil; - if not (csDestroying in ComponentState) then - Check(Engine, r); - end; -end; - -constructor TPSQLStoredProc.Create(AOwner: TComponent); -begin - inherited; - FParams := TPSQLParams.Create(Self); - FNeedRefreshParams := false; -end; - -function TPSQLStoredProc.CreateBlobStream(Field: TField; - Mode: TBlobStreamMode): TStream; -begin - if Mode = bmRead then - Result := TPSQLBlobStream.Create(Field as TBlobField, Mode) - else - raise EPSQLDatabaseError.CreateFmt(SCantCreateWriteBLOB,[]); -end; - -function TPSQLStoredProc.CreateCursor(IsExecProc : boolean): HDBICur; -var - PCursor: phDBICur; - AffectedRows: integer; -begin - Result := nil; - - if Database=nil then - DatabaseError(Format(SDatabaseProperty, [Self.Name]),Self); - - if Length(FProcName) = 0 then - begin - DatabaseError(SEmptyprocedureName, Self); - exit; - end; - - RefreshParams; - - try - SetBoolProp(Engine, hDBIStmt(FHandle), stmtLIVENESS, false);//procedure results alway readonly - while not CheckOpen(Engine.QPrepareProc(GetDBHandle, PChar(FProcName), FParams, hDBIStmt(FHandle))) do - {Retry}; - except - if FHandle <> nil then - begin - Engine.CloseCursor(FHandle); - FHandle := nil; - end; - raise; - end; - - if IsExecProc then - PCursor := nil - else - PCursor := @Result; - - Check(Engine, Engine.QSetProcParams(hDBIStmt(FHandle), FParams)); - - Check(Engine, Engine.QExec(hDBIStmt(FHandle), PCursor, AffectedRows)); -end; - -function TPSQLStoredProc.CreateHandle: HDBICur; -begin - Result := HDBICur(CreateCursor(false)); -end; - -procedure TPSQLStoredProc.DataEvent(Event: TDataEvent; Info: TDataEventInfo); -var - F: TField; - i: integer; -begin - inherited DataEvent(Event, Info); - if (Event = deUpdateState) and (State = dsBrowse) then - for i := 0 to FParams.Count - 1 do - if FParams[i].ParamType in [ptOutput, ptInputOutput] then - begin - F := FieldByName(FParams[i].Name); - if Assigned(F) then - Params[i].Value := F.Value; - end; -end; - -function TPSQLStoredProc.DescriptionsAvailable: Boolean; -begin - Result := True; -end; - -destructor TPSQLStoredProc.Destroy; -begin - FParams.Free; - inherited; -end; - -function TPSQLStoredProc.Engine: TPSQLEngine; -begin - Result := FDataBase.Engine; -end; - -procedure TPSQLStoredProc.ExecProc; -begin - CheckInActive; - - SetDBFlag(dbfExecSQL, TRUE); - try - CreateCursor(true); - finally - SetDBFlag(dbfExecSQL, FALSE); - - if FHandle <> nil then - begin - Check(Engine, Engine.CloseCursor(hDBICur(FHandle))); - FHandle := nil; - end; - end; -end; - -function TPSQLStoredProc.GetCanModify: Boolean; -begin - Result := false; //mi:2006-10-30 we never able modify resultset of stored procedure -end; - -function TPSQLStoredProc.GetParamsCount: integer; -begin - if [csDesigning, csReading] * ComponentState = [] then - RefreshParams; - Result := FParams.Count; -end; - -function TPSQLStoredProc.GetParamsList: TPSQLParams; -begin - if [csDesigning, csReading] * ComponentState = [] then - RefreshParams; - Result := FParams; -end; - -function TPSQLStoredProc.ParamByName(const Value: string): TPSQLParam; -begin - Result := FParams.ParamByName(Value); -end; - -{$IFNDEF FPC} -procedure TPSQLStoredProc.PSExecute; -begin - inherited; - -end; - -function TPSQLStoredProc.PSGetParams: TParams; -begin - Result := FParams; -end; - -function TPSQLStoredProc.PSGetTableName: string; -begin - Result := FProcName; -end; - -procedure TPSQLStoredProc.PSSetCommandText(const CommandText: string); -begin - inherited; - -end; - -procedure TPSQLStoredProc.PSSetParams(AParams: TParams); -begin - inherited; - -end; -{$ENDIF} - -procedure TPSQLStoredProc.RefreshParams; -var - Desc: ^SPParamDesc; - ParamName: string; - ParamDataType: TFieldType; - List : TList{$IFDEF NEXTGEN}{$ENDIF}; - i:integer; -begin - if not FNeedRefreshParams or not FDatabase.Connected then Exit; - List := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - try - FParams.Clear; - Check(Engine, Engine.OpenStoredProcParams(DBHandle, StoredProcName, FOverload, List)); - for i:=0 to List.Count-1 do - begin - Desc := List[i]; - with Desc^ do - begin - ParamName := szName; - if (TParamType(eParamType) = ptResult) and (ParamName = '') then - ParamName := SResultName; - if uFldType < MAXLOGFLDTYPES then ParamDataType := DataTypeMap[uFldType] - else ParamDataType := ftUnknown; - case uFldtype of - fldFloat: - if uSubType = fldstMONEY then ParamDataType := ftCurrency; - fldBlob: - if (uSubType >= fldstMEMO) and (uSubType <= fldstBFILE) then - ParamDataType := BlobTypeMap[uSubType]; - end; - with TParam(FParams.Add) do - begin - ParamType := TParamType(eParamType); - DataType := ParamDataType; - Name := string(ParamName); - end; - end; - end; - finally - for i:=0 to List.Count-1 do - begin - Desc := List[i]; - Dispose(Desc); - end; - List.Free; - FNeedRefreshParams := false; - end; -end; - -procedure TPSQLStoredProc.SetNeedRefreshParams; -begin - FNeedRefreshParams := true; -end; - -procedure TPSQLStoredProc.SetOverload(const Value: cardinal); -begin - if ([csReading, csLoading] * ComponentState = []) then - SetNeedRefreshParams(); - FOverload := Value; - RefreshParams; - DataEvent(dePropertyChange, 0); -end; - -procedure TPSQLStoredProc.SetParamsList(const Value: TPSQLParams); -begin - FParams.AssignValues(Value); -end; - -procedure TPSQLStoredProc.SetProcedureName(const Value: string); -begin - if ([csReading, csLoading] * ComponentState = []) then - SetNeedRefreshParams(); - FProcName := Value; - RefreshParams; - DataEvent(dePropertyChange, 0); -end; - -procedure TPSQLStoredProc.SetProcName(const Value: string); -begin - if ([csReading, csLoading] * ComponentState = []) then - SetNeedRefreshParams(); - FProcName := Value; - RefreshParams; - DataEvent(dePropertyChange, 0); -end; - -function TPSQLTable.GetOffset: Integer; -begin - Result := FOffset; -end; - -procedure TPSQLTable.SetOffset(const Value: Integer); -begin - FOffset := Value; -end; - -procedure TPSQLTable.SetDummyInt(const Value: cardinal); -begin -//dummy method for published -end; - -procedure TPSQLTable.SetDummyStr(const Value: string); -begin -//dummy method for published -end; - -function TPSQLTable.GetTableSpace: string; -begin - if (FTableSpace = '') and Database.Connected then - Result := Database.Tablespace - else - Result := FTablespace; -end; - -procedure TPSQLTable.SetOptions(const Value: TPSQLDatasetOptions); -begin - if Value = FOptions then Exit; - inherited; - if Active then - begin - Close; - Open; - end; -end; - -{ TPSQLParams } -constructor TPSQLParams.Create(Owner: TPersistent); -begin - FOwner := Owner; - inherited Create(TPSQLParam); -end; - -procedure TPSQLParams.AssignValues(Value: TParams); -begin - inherited; -end; - -function TPSQLParams.GetOwner: TPersistent; -begin - Result := FOwner; -end; - -constructor TPSQLParams.Create; -begin - inherited Create(TPSQLParam); - FOwner := nil; -end; - -function TPSQLParams.CreateParam(FldType: TFieldType; const ParamName: string; - ParamType: TParamType; const DataTypeOID: cardinal = 0; Binary: boolean = False): TPSQLParam; -begin - Result := inherited CreateParam(FldType, ParamName, ParamType) as TPSQLParam; - Result.DataTypeOID := DataTypeOID; - Result.Binary := Binary; -end; - -function TPSQLParams.GetItem(Index: Integer): TPSQLParam; -begin - Result := inherited GetItem(index) as TPSQLParam; -end; - -function TPSQLParams.ParamByName(const Value: string): TPSQLParam; -begin - Result := inherited ParamByName(Value) as TPSQLParam; -end; - -function TPSQLParams.ParseSQL(SQL: string; DoCreate: Boolean): string; -const - {$IFNDEF NEXTGEN} - Literals = ['''', '"', '`'] - {$ELSE} - Literals : array of Char = ['''', '"', '`'] - {$ENDIF}; -var - Value, CurPos, StartPos: PChar; - CurChar: Char; - Literal: Boolean; - EmbeddedLiteral: Boolean; - Name: string; - - function NameDelimiter: Boolean; - begin - {$IFNDEF NEXTGEN} - Result := CharInSet(CurChar, [' ', ',', ';', ')', #13, #10]); - {$ELSE} - Result := CurChar.IsInArray([' ', ',', ';', ')', #13, #10]); - {$ENDIF} - end; - - function IsLiteral: Boolean; - begin - {$IFNDEF NEXTGEN} - Result := CharInSet(CurChar, Literals); - {$ELSE} - Result := CurChar.IsInArray(Literals); - {$ENDIF} - end; - - function StripLiterals(Buffer: PChar): string; - var - Len: integer; - TempBuf: PChar; - - procedure StripChar; - begin - {$IFNDEF NEXTGEN} - if CharInSet(TempBuf^, Literals) then - {$ELSE} - if TempBuf^.IsInArray(Literals) then - {$ENDIF} - begin - StrMove(TempBuf, TempBuf + 1, Len - 1); - {$IFNDEF NEXTGEN} - if CharInSet((TempBuf + (Len-2))^, Literals) then - {$ELSE} - if (TempBuf + (Len-2))^.IsInArray(Literals) then - {$ENDIF} - (TempBuf + Len-2)^ := #0; - end; - end; - - begin - Len := StrLen(Buffer); - TempBuf := AllocMem((Len + 1) * SizeOf(Char)); - try - StrCopy(TempBuf, Buffer); - StripChar; - Result := TempBuf; - finally - FreeMem(TempBuf, (Len + 1) * SizeOf(Char)); - end; - end; - -begin - - Result := SQL; - Value := PChar(Result); - if DoCreate then Clear; - CurPos := Value; - Literal := False; - EmbeddedLiteral := False; - repeat - CurChar := CurPos^; - if (CurChar = ':') and not Literal and - {$IFNDEF NEXTGEN} - not CharInSet((CurPos + 1)^, [':', '=', ' ', ',', ';', #9, #13, #10]) then - {$ELSE} - not (CurPos + 1)^.IsInArray([':', '=', ' ', ',', ';', #9, #13, #10]) then - {$ENDIF} - begin - StartPos := CurPos; - while (CurChar <> #0) and (Literal or not NameDelimiter) do - begin - Inc(CurPos); - CurChar := CurPos^; - if IsLiteral then - begin - Literal := Literal xor True; - if CurPos = StartPos + 1 then EmbeddedLiteral := True; - end; - end; - CurPos^ := #0; - if EmbeddedLiteral then - begin - Name := StripLiterals(StartPos + 1); - EmbeddedLiteral := False; - end - else Name := string(StartPos + 1); - if DoCreate and not Assigned(FindParam(Name)) then - TParam(Add).Name := Name; - CurPos^ := CurChar; - StartPos^ := '?'; - Inc(StartPos); - StrMove(StartPos, CurPos, StrLen(CurPos) + 1); - CurPos := StartPos; - end - else if (CurChar = ':') and not Literal and - {$IFNDEF NEXTGEN} - CharInSet((CurPos + 1)^, [':', '=', ' ', ',', ';', #9, #13, #10]) then - {$ELSE} - ((CurPos + 1)^.IsInArray([':', '=', ' ', ',', ';', #9, #13, #10])) then - {$ENDIF} - StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1) - else if IsLiteral then Literal := Literal xor True; - Inc(CurPos); - until CurChar = #0; -end; - -procedure TPSQLParams.SetItem(Index: Integer; const Value: TPSQLParam); -begin - inherited SetItem(Index, TCollectionItem(Value)); -end; - -{ TPSQLParam } - -function TPSQLParam.IsEqual(Value: TParam): Boolean; -begin - Result := inherited IsEqual(Value); - if Value is TPSQLParam then - Result := Result - and (FDataTypeOid = TPSQLParam(Value).DataTypeOid) - and (FBinary = TPSQLParam(Value).Binary); -end; - -procedure TPSQLParam.SetDataTypeOID(const Value: cardinal); -begin - FDataTypeOID := Value; -end; - -{$IFDEF DELPHI_12} -{ TPSQLLookupList } - -procedure TPSQLLookupList.Add(const AKey, AValue: Variant); -var - ListEntry: PLookupListEntry; -begin - New(ListEntry); - ListEntry.Key := AKey; - ListEntry.Value := AValue; - FList.Add(ListEntry); -end; - -procedure TPSQLLookupList.Clear; -var - I: Integer; -begin - for I := 0 to FList.Count - 1 do - Dispose(PLookupListEntry(FList.Items[I])); - FList.Clear; -end; - -constructor TPSQLLookupList.Create; -begin - inherited; - FList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; -end; - -destructor TPSQLLookupList.Destroy; -begin - if FList <> nil then Clear; - FList.Free; -end; - -function TPSQLLookupList.ValueOfKey(const AKey: Variant): Variant; -var - I: Integer; -begin - Result := Null; - if VarIsNull(AKey) then Exit; - if VarIsArray(AKey) then - begin - for I := 0 to FList.Count - 1 do - if IsSameVarArrays(PLookupListEntry(FList.Items[I]).Key, AKey) then - begin - Result := PLookupListEntry(FList.Items[I]).Value; - Exit; - end; - end - else - for I := 0 to FList.Count - 1 do - if PLookupListEntry(FList.Items[I]).Key = AKey then - begin - Result := PLookupListEntry(FList.Items[I]).Value; - Break; - end; -end; - -{ TPSQLFieldDef } - -procedure TPSQLFieldDef.SetNativeDataType(const Value: cardinal); -begin - FNativeDataType := Value; - Changed(False); -end; - -{ TPSQLFieldDefs } - -function TPSQLFieldDefs.GetFieldDefClass: TFieldDefClass; -begin - Result := TPSQLFieldDef; -end; -{$ENDIF DELPHI_12} - -initialization - - if not IsLibrary then - begin - SaveInitProc := InitProc; - InitProc := @InitDBTables; - end; - DBList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - -finalization - - DBList.Free; - DBList := nil; - - if not IsLibrary then - InitProc := SaveInitProc; - -end. +{$I PSQLDAC.inc} +unit PSQLDbTables; + +{SVN revision: $Id$} + +{$R-,T-,H+,X+} +{$C+} +interface + +uses SysUtils, Classes, Db, + {$IFDEF DELPHI_9}DbCommon{$ELSE}PSQLCommon{$ENDIF}, + {$IFDEF DELPHI_6}Variants,{$ENDIF} + {$IFDEF FPC}Variants,{$ENDIF} + {$IFDEF NEXTGEN}Generics.Collections,{$ENDIF} + {$IFDEF DELPHI_17}System.Types, System.Generics.Collections,{$ENDIF} + PSQLAccess, PSQLTypes; + +const + VERSION : string = '3.12'; + +{ TDBDataSet flags } + dbfOpened = 0; + dbfPrepared = 1; + dbfExecSQL = 2; + dbfTable = 3; + dbfFieldList = 4; + dbfIndexList = 5; + dbfStoredProc = 6; + dbfExecProc = 7; + dbfProcDesc = 8; + dbfDatabase = 9; + dbfProvider = 10; + +{ FieldType Mappings } + +const + {$IFDEF FPC} + FldTypeMap: TFieldMap = ( + fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, //0..4 + fldBOOL, fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, //5..11 + fldBYTES, fldVARBYTES, fldINT32, fldBLOB, fldBLOB, fldBLOB, fldBLOB, //12..18 + fldBLOB, fldBLOB, fldBLOB, fldCURSOR, fldZSTRING, fldZSTRING, //19..24 + fldINT64, fldADT, fldArray, fldREF, fldTABLE, fldBLOB, fldBLOB, //25..31 + fldUNKNOWN, fldUNKNOWN, fldUNKNOWN, fldZSTRING, fldDATETIME, fldBCD, + fldZSTRING, fldBLOB); + + FldSubTypeMap: array[TFieldType] of Word = ( + 0, 0, 0, 0, 0, 0, 0, fldstMONEY, 0, 0, 0, 0, 0, 0, fldstAUTOINC, + fldstBINARY, fldstMEMO, fldstGRAPHIC, fldstFMTMEMO, fldstOLEOBJ, + fldstDBSOLEOBJ, fldstTYPEDBINARY, 0, fldstFIXED, fldstUNICODE, + 0, 0, 0, 0, 0, fldstHBINARY, fldstHMEMO, 0, 0, 0, 0, 0, 0, + fldstFIXED, fldstMEMO); + + {$ELSE} + + FldTypeMap: TFieldMap = ( + fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, //0..4 + fldBOOL, fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, //5..11 + fldBYTES, fldVARBYTES, fldINT32, fldBLOB, fldBLOB, fldBLOB, fldBLOB, //12..18 + fldBLOB, fldBLOB, fldBLOB, fldCURSOR, fldZSTRING, fldZSTRING, //19..24 + fldINT64, fldADT, fldArray, fldREF, fldTABLE, fldBLOB, fldBLOB, //25..31 + fldUNKNOWN, fldUNKNOWN, fldUNKNOWN, fldZSTRING + {$IFDEF DELPHI_6}, fldDATETIME, fldBCD{$ENDIF} //26..37 + {$IFDEF DELPHI_10}, fldZSTRING, fldBLOB, fldDATETIME, fldINT32{$ENDIF} //38..41 + {$IFDEF DELPHI_12}, fldINT32, fldINT16, fldUNKNOWN, fldFLOATIEEE, fldUNKNOWN, fldUNKNOWN, fldUNKNOWN{$ENDIF} //42..48 + {$IFDEF DELPHI_14}, fldUNKNOWN, fldUNKNOWN, fldUNKNOWN{$ENDIF} //49..52 + ); + + FldSubTypeMap: array[TFieldType] of Word = ( + 0, 0, 0, 0, 0, 0, 0, fldstMONEY, 0, 0, 0, 0, 0, 0, fldstAUTOINC, + fldstBINARY, fldstMEMO, fldstGRAPHIC, fldstFMTMEMO, fldstOLEOBJ, + fldstDBSOLEOBJ, fldstTYPEDBINARY, 0, fldstFIXED, fldstUNICODE, + 0, 0, 0, 0, 0, fldstHBINARY, fldstHMEMO, 0, 0, 0, 0{$IFDEF DELPHI_6} , 0, 0{$ENDIF} + {$IFDEF DELPHI_10}, fldstFIXED, fldstMEMO, 0, 0 {$ENDIF} //38..41 + {$IFDEF DELPHI_12}, 0, 0, 0, 0, 0, 0, 0{$ENDIF} //42..48 + {$IFDEF DELPHI_14}, 0, 0, 0{$ENDIF} //49..52 + ); + {$ENDIF} + + + DataTypeMap: array[0..MAXLOGFLDTYPES - 1] of TFieldType = ( + ftUnknown, ftString, ftDate, ftBlob, ftBoolean, ftSmallint, + ftInteger, ftFloat, ftBCD, ftBytes, ftTime, ftDateTime, + ftWord, ftInteger, ftUnknown, ftVarBytes, ftUnknown, ftUnknown, + ftLargeInt, ftLargeInt, ftADT, ftArray, ftReference, ftDataSet + {$IFDEF DELPHI_6},ftTimeStamp{$ENDIF} + {$IFDEF DELPHI_12},ftFMTBcd, ftWideString{$ENDIF}); + + BlobTypeMap: array[fldstMEMO..fldstBFILE] of TFieldType = ( + ftMemo, ftBlob, ftFmtMemo, ftParadoxOle, ftGraphic, ftDBaseOle, + ftTypedBinary, ftBlob, ftBlob, ftBlob, ftBlob, ftOraClob, ftOraBlob, + ftBlob, ftBlob); + +type + + //used for LOCK TABLE + TPSQLLockType = (ltAccessShare, ltRowShare, ltRowExclusive, ltShareUpdateExclusive, + ltShare, ltShareRowExclusive, ltExclusive, ltAccessExclusive); + + //used for DataEvent handlers + TDataEventInfo = + {$IFDEF FPC}Ptrint{$ELSE} + {$IFDEF DELPHI_16}NativeInt + {$ELSE}LongInt + {$ENDIF} + {$ENDIF}; + + { Forward declarations } + TPSQLDatabase = Class; + TPSQLDatabaseClass = Class of TPSQLDatabase; + TPSQLDataSet = Class; + TPSQLTable = Class; + TPSQLTableClass = Class of TPSQLTable; + TPSQLQuery = Class; + TPSQLQueryClass = Class of TPSQLQuery; + + { Exception Classes } + EPSQLDatabaseError = Class(EDatabaseError) + private + FErrorCode: Word; + FErrorPos:string; + FErrorContext:string; + FErrorseverity:string; + FErrorsqlstate:string; + FErrorprimary:string; + FErrordetail:string; + FErrorhint:string; + FErrorinternalpos:string; + FErrorinternalquery:string; + FErrorsourcefile:string; + FErrorsourceline:string; + FErrorsourcefunc:string; + FErrorDataTypeName: string; + FErrorColumnName: string; + FErrorSchemaName: string; + FErrorTableName: string; + FErrorConstraintName: string; + public + constructor Create(Engine : TPSQLEngine; ErrorCode: Word); + destructor Destroy; override; + property ErrorCode: Word read FErrorCode; + property ErrorPos : string read FErrorPos; + property ErrorContext : string read FErrorContext; + property ErrorSeverity : string read FErrorseverity; + property ErrorSqlState : string read FErrorsqlstate; + property ErrorPrimary : string read FErrorprimary; + property ErrorDetail : string read FErrordetail; + property ErrorHint : string read FErrorhint; + property ErrorInternalPos : string read FErrorinternalpos; + property ErrorInternalQuery : string read FErrorinternalquery; + property ErrorSourceFile : string read FErrorsourcefile; + property ErrorSourceLine : string read FErrorsourceline; + property ErrorSourceFunc : string read FErrorsourcefunc; + property ErrorSchemaName : string read FErrorSchemaName; + property ErrorTableName : string read FErrorTableName; + property ErrorColumnName : string read FErrorColumnName; + property ErrorDataTypeName : string read FErrorDataTypeName; + property ErrorConstraintName : string read FErrorConstraintName; + end; + + ENoResultSet = class(EDatabaseError); + +{$IFDEF DELPHI_12} + TPSQLLookupList = class(TLookupList) + private + FList: TList{$IFDEF NEXTGEN}{$ENDIF}; + public + constructor Create; override; + destructor Destroy; override; + procedure Add(const AKey, AValue: Variant); override; + procedure Clear; override; + function ValueOfKey(const AKey: Variant): Variant; override; + end; + + TPSQLFieldDef = class(TFieldDef) + private + FNativeDataType: cardinal; + procedure SetNativeDataType(const Value: cardinal); + published + property NativeDataType: cardinal read FNativeDataType write SetNativeDataType default 0; + end; + + TPSQLFieldDefs = class(TFieldDefs) + protected + function GetFieldDefClass: TFieldDefClass; override; + end; +{$ENDIF} + + TParamClass = class of TParam; + + TPSQLParam = class(TParam) + private + FDataTypeOID: cardinal; + FBinary: boolean; + procedure SetDataTypeOID(const Value: cardinal); + protected + function IsEqual(Value: TParam): Boolean; + published + property DataTypeOID: cardinal read FDataTypeOID write SetDataTypeOID default 0; + property Binary: boolean read FBinary write FBinary default False; + end; + + TPSQLParams = class(TParams) + private + FOwner: TPersistent; + function GetItem(Index: Integer): TPSQLParam; + procedure SetItem(Index: Integer; const Value: TPSQLParam); + protected + function GetOwner: TPersistent; override; + public + constructor Create(Owner: TPersistent); overload; + constructor Create; overload; + procedure AssignValues(Value: TParams); + function CreateParam(FldType: TFieldType; const ParamName: string; + ParamType: TParamType; const DataTypeOID: cardinal = 0; Binary: boolean = False): TPSQLParam; + function ParamByName(const Value: string): TPSQLParam; + function ParseSQL(SQL: string; DoCreate: Boolean): string; reintroduce; + property Items[Index: Integer]: TPSQLParam read GetItem write SetItem; default; + end; + + TDatabaseNoticeEvent = procedure (Sender: TPSQLDatabase; Message: string) of object; + TBaseDatabaseLoginEvent = procedure(Database: TPSQLDatabase; LoginParams: TStrings) of object; + TDbExceptionEvent = procedure (Sender: TObject; E: Exception) of object; + + TDBFlags = set of 0..15; + + TTransIsolation = (tiDirtyRead, tiReadCommitted, tiRepeatableRead, tiSerializable); + + + TPSQLDBDesignOption = (ddoStoreConnected, ddoStorePassword); + TPSQLDBDesignOptions = set of TPSQLDBDesignOption; + + IPSQLLoginDialog = interface + ['{A5CDBD6A-3212-47EA-9CC0-04C11AE11FC7}'] + function Execute(const DB: TPSQLDatabase): boolean; + end; + + TPSQLDatabase = Class(TCustomConnection) + private + FAbout : TPSQLDACAbout; + FTransIsolation: TTransIsolation; + FKeepConnection: Boolean; //AutoStop + FOEMConvert : Boolean; //OEM->ANSI + FCharSet: string; + FCommandTimeout: cardinal; + FEngine : TPSQLEngine; //Postgres Engine + FTemporary: Boolean; + FAcquiredHandle: Boolean; + FPseudoIndexes: Boolean; + FHandleShared: Boolean; + FExclusive: Boolean; + FReadOnly: Boolean; + FRefCount: Integer; + FHandle: DAChDBIDb; + FParams: TStrings; + FStmtList: TList{$IFDEF NEXTGEN}{$ENDIF}; + FOwner: string; + FIsTemplate: boolean; + FTablespace: string; + FDatabaseID: cardinal; + FComment: string; + FServerVersion: string; + FDesignOptions: TPSQLDBDesignOptions;//design time info for DB + FOnAdd: TNotifyEvent; + FOnLogin: TBaseDatabaseLoginEvent; + FOnNotice: TDatabaseNoticeEvent; + FNotifyList: TList{$IFDEF NEXTGEN}{$ENDIF}; //List of notify + FDirectQueryList : TList{$IFDEF NEXTGEN}{$ENDIF}; + FCheckIfActiveOnParamChange: boolean; + FSSLMode: TSSLMode; + FErrorVerbosity: TErrorVerbosity; + FOnException: TDbExceptionEvent; + FUseSingleLineConnInfo: boolean; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FLoginDialog: IPSQLLoginDialog; + function GetNotifyItem(Index: Integer): TObject; + function GetNotifyCount: Integer; + procedure FillAddonInfo; + procedure CheckActive; + procedure CheckInactive; + procedure ClearStatements; + procedure EndTransaction(TransEnd: EXEnd); + function GetInTransaction: Boolean; + function GetTransactionStatus: TTransactionStatusType; + function GetServerVersionAsInt: integer; + function GetLibraryVersionAsInt: integer; + procedure Login(LoginParams: TStrings); + procedure ParamsChanging(Sender: TObject); + procedure SetDatabaseFlags; + procedure SetDatabaseName(const Value: string); + procedure SetUserName(const Value: string); + procedure SetUserPassword(const Value: string); + procedure SetServerPort(const Value: Cardinal); + procedure SetConnectionTimeout(const Value: cardinal); + procedure SetCommandTimeout(const Value: cardinal); + procedure SetHost(const Value : string); + procedure SetKeepConnection(Value: Boolean); + procedure SetExclusive(Value: Boolean); + procedure SetHandle(Value: DAChDBIDb); + procedure SetParams(Value: TStrings); + procedure SetReadOnly(Value: Boolean); + procedure SetDummyStr(Value: string); + procedure SetDummyBool(Value: boolean); + procedure SetDummyInt(Value: cardinal); + procedure SetSSLMode(const Value: TSSLMode); + procedure SetErrorVerbosity(const Value: TErrorVerbosity); + function GetDatabaseID: cardinal; + function GetIsTemplate: boolean; + function GetDBOwner: string; + function GetTablespace: string; + function GetIsUnicodeUsed: Boolean; + function GetDatabaseComment: string; + function GetIsSSLUsed: Boolean; + procedure SetUseSSL(Reader: TReader); + function GetUsrName: string; + function GetUserPassword: string; + function GetDatabaseName: string; + function GetSSLOption(Index: integer): string; + procedure SetSSLOption(Index: integer; Value: string); + function GetConnectionTimeout: cardinal; + function GetServerPort: Cardinal; + function GetHost: string; + function GetGUCParamValue(const Name: string): string; + protected + procedure DefineProperties(Filer: TFiler); override; //deal with old missing properties + procedure CloseDatabaseHandle; + procedure CloseDatabase(Database: TPSQLDatabase); + procedure DoConnect; override; + procedure DoDisconnect; override; + function GetConnected: Boolean; override; + function GetStoreConnected: boolean; + function GetDataSet(Index: Integer): TPSQLDataSet; reintroduce; + procedure Loaded; override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure InitEngine; //Init SQL Engine + procedure AddDatabase(Value : TPSQLDatabase); + procedure RemoveDatabase(Value : TPSQLDatabase); + property CheckIfActiveOnParamChange: boolean read FCheckIfActiveOnParamChange write FCheckIfActiveOnParamChange; + procedure WriteState(Writer: TWriter); override; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + + procedure AssignTo(Dest: TPersistent); override; + + function Engine : TPSQLEngine; + function Execute(const SQL: string; Params: TParams = nil; Cache: Boolean = FALSE; Cursor: phDBICur = nil): Integer; + function GetBackendPID: Integer; + + function SelectString(aSQL: string; var IsOk: boolean; aFieldName: string): string; overload; + function SelectString(aSQL: string; var IsOk: boolean; aFieldNumber: integer = 0): string; overload; + function SelectStringDef(aSQL: string; aDefaultValue: string; aFieldName: string): string; overload; + function SelectStringDef(aSQL: string; aDefaultValue: string; aFieldNumber: integer = 0): string; overload; + + function Ping: TPingStatus; overload; + function Ping(ConnectionParams: TStrings): TPingStatus; overload; + + procedure SelectStrings(aSQL: string; aList: TStrings; aFieldName: string); overload; + procedure SelectStrings(aSQL: string; aList: TStrings; aFieldNumber: integer = 0); overload; + + procedure AddNotify(AItem: TObject); + procedure ApplyUpdates(const DataSets: array of TPSQLDataSet); + procedure CancelBackend(PID : Integer); + procedure CloseDataSets; + procedure CloseNotify; + procedure Commit; + procedure GetCharsets(List: TStrings); + procedure GetDatabases(Pattern: string;List : TStrings); + procedure GetSchemaNames(Pattern: string; SystemSchemas: Boolean; List: TStrings); + procedure GetStoredProcNames(Pattern: string; List: TStrings); + procedure GetTableNames(Pattern: string; SystemTables: Boolean; List: TStrings); + procedure GetTablespaces(Pattern: string; List: TStrings); + procedure GetUserNames(Pattern: string; List: TStrings); + procedure RegisterDirectQuery(aDirectQuery : TObject); + procedure RemoveNotify(AItem: TObject); + procedure Reset; + procedure ReloadGUC; + procedure Rollback; + procedure SetCharSet(CharSet: string); + procedure StartTransaction; + procedure UnregisterDirectQuery(aDirectQuery : TObject); + + procedure Savepoint(const Name: string); + procedure ReleaseSavepoint(const Name: string); + procedure RollbackToSavepoint(const Name: string); + + property DataSets[Index: Integer]: TPSQLDataSet read GetDataSet; + property Handle: DAChDBIDb read FHandle write SetHandle; + property InTransaction: Boolean read GetInTransaction; + property IsUnicodeUsed: Boolean read GetIsUnicodeUsed; + property IsSSLUsed: Boolean read GetIsSSLUsed; + property Notifies[Index: Integer]: TObject read GetNotifyItem; + property NotifyCount: Integer read GetNotifyCount; + property ServerVersionAsInt: integer read GetServerVersionAsInt; + property LibraryVersionAsInt: integer read GetLibraryVersionAsInt; + property Temporary: Boolean read FTemporary write FTemporary; + property TransactionStatus: TTransactionStatusType read GetTransactionStatus; + property UseSingleLineConnInfo: boolean read FUseSingleLineConnInfo write FUseSingleLineConnInfo; + property GUC[const Name: string]: string read GetGUCParamValue; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property LoginDialog: IPSQLLoginDialog read FLoginDialog write FLoginDialog; + property AfterConnect; + property AfterDisconnect; + property BeforeConnect; + property BeforeDisconnect; + property CharSet: string read FCharSet write SetCharSet; + property CommandTimeout: cardinal read FCommandTimeout write SetCommandTimeout default 0; + property Comment: string read GetDatabaseComment write SetDummyStr stored False; + property Connected stored GetStoreConnected; + property DatabaseID: cardinal read GetDatabaseID write SetDummyInt stored False; + property DesignOptions: TPSQLDBDesignOptions read FDesignOptions write FDesignOptions default [ddoStoreConnected, ddoStorePassword]; + property ErrorVerbosity: TErrorVerbosity read FErrorVerbosity write SetErrorVerbosity default evDEFAULT; + property Exclusive: Boolean read FExclusive write SetExclusive default False; + property HandleShared: Boolean read FHandleShared write FHandleShared default False; + property IsTemplate: boolean read GetIsTemplate write SetDummyBool stored False; + property KeepConnection: Boolean read FKeepConnection write SetKeepConnection default True; + property LoginPrompt; + property OEMConvert: Boolean read FOEMConvert write FOEMConvert default False; + property OnAdd: TNotifyEvent read FOnAdd; + property OnException: TDbExceptionEvent read FOnException write FOnException; + property OnLogin: TBaseDatabaseLoginEvent read FOnLogin write FOnLogin; + property OnNotice: TDatabaseNoticeEvent read FOnNotice write FOnNotice; + property Owner: string read GetDBOwner write SetDummyStr stored False; + property ReadOnly: Boolean read FReadOnly write SetReadOnly default FALSE; + property ServerVersion: string read FServerVersion write SetDummyStr stored False; + property Tablespace: string read GetTablespace write SetDummyStr stored False; + property TransIsolation: TTransIsolation read FTransIsolation write FTransIsolation default tiReadCommitted; + //connection parameters + property ConnectionTimeout: cardinal read GetConnectionTimeout write SetConnectionTimeout stored False default 15; + property DatabaseName: string read GetDatabaseName write SetDatabaseName stored False; + property Host: string read GetHost write SetHost stored False; + property Params: TStrings read FParams write SetParams; + property Port : Cardinal read GetServerPort write SetServerPort stored False default PSQL_PORT; + property UserName : string read GetUsrName write SetUserName stored False; //method name changed due to bug in ILINK32 + property UserPassword : string read GetUserPassword write SetUserPassword stored False; + property SSLMode: TSSLMode read FSSLMode write SetSSLMode default sslPrefer; + property SSLCert: string index 0 read GetSSLOption write SetSSLOption stored False; + property SSLKey: string index 1 read GetSSLOption write SetSSLOption stored False; + property SSLRootCert: string index 2 read GetSSLOption write SetSSLOption stored False; + property SSLCRL: string index 3 read GetSSLOption write SetSSLOption stored False; + end; + + { TPSQLBDECallBack } + TPSQLBDECallbackEvent = function(CBInfo: Pointer): CBRType of Object; + + TPSQLBDECallBack = Class(TObject) + Private + FHandle: hDBICur; + FOwner: TObject; + FCBType: CBType; + FOldCBData: Longint; + FOldCBFunc: pfDBICallBack; + FCallbackEvent: TPSQLBDECallbackEvent; + FEngine : TPSQLEngine; + protected + function Invoke(CallType: CBType; CBInfo: Pointer): CBRType; + public + constructor Create(Engine : TPSQLEngine; AOwner: TObject; Handle: hDBICur; CBType: CBType; + CBBuf: Pointer; CBBufSize: Integer; CallbackEvent: TPSQLBDECallbackEvent;Chain: Boolean); + destructor Destroy; override; + end; + + TRecNoStatus = (rnDbase, rnParadox, rnNotSupported); + + TPSQLSQLUpdateObject = class(TComponent) + protected + function GetDataSet: TPSQLDataSet; virtual; abstract; + procedure SetDataSet(ADataSet: TPSQLDataSet); virtual; abstract; + procedure Apply(UpdateKind: TUpdateKind); virtual; abstract; + function GetSQL(UpdateKind: TUpdateKind): TStrings; virtual; abstract; + property DataSet: TPSQLDataSet read GetDataSet write SetDataSet; + end; + + TKeyIndex = (kiLookup, kiRangeStart, kiRangeEnd, kiCurRangeStart, + kiCurRangeEnd, kiSave); + + PKeyBuffer = ^TKeyBuffer; + TKeyBuffer = packed record + Modified: Boolean; + Exclusive: Boolean; + FieldCount: Integer; + end; + + PRecInfo = ^TRecInfo; + TRecInfo = packed record + RecordNumber: Longint; + UpdateStatus: TUpdateStatus; + BookmarkFlag: TBookmarkFlag; + end; + + TBlobDataArray = array of TBlobData; + + TPSQLDataSet = class(TDataSet) + private + FAbout : TPSQLDACAbout; + FHandle: HDBICur; + FRecProps: RecProps; //Record properties + FExprFilter: HDBIFilter; //Filter expression + FFuncFilter: HDBIFilter; // filter function + FFilterBuffer: TRecordBuffer; // filter buffer + FIndexFieldMap: DBIKey; //Index field map + FExpIndex: Boolean; + FCaseInsIndex: Boolean; + FCachedUpdates: Boolean; + FInUpdateCallback: Boolean; + FCanModify: Boolean; + FCacheBlobs: Boolean; + FKeySize: integer; + FUpdateCBBuf: PDELAYUPDCbDesc; + FUpdateCallback: TPSQLBDECallBack; + FKeyBuffers: array[TKeyIndex] of PKeyBuffer; + FKeyBuffer: PKeyBuffer; + FRecNoStatus: TRecNoStatus; + FIndexFieldCount: Integer; + FRecordSize: integer; + FBookmarkOfs: integer; + FRecInfoOfs: integer; + FBlobCacheOfs: integer; + FRecBufSize: integer; + FBlockBufOfs: Integer; + FLastParentPos: Integer; + FBlockReadBuf: PAnsiDACChar; + {$IFNDEF FPC} + FBlockBufSize: Integer; + FBlockBufCount: Integer; + FBlockReadCount: Integer; + {$ENDIF} + FOldBuffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; + FParentDataSet: TPSQLDataSet; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FUpdateObject: TPSQLSQLUpdateObject; + {$IFNDEF FPC} + FOnUpdateError: TUpdateErrorEvent; + FOnUpdateRecord: TUpdateRecordEvent; + {$ENDIF} + FAutoRefresh: Boolean; + FDBFlags: TDBFlags; + FUpdateMode: TUpdateMode; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; + FAllowSequenced: boolean; + FSortFieldNames: string; + FOptions: TPSQLDatasetOptions; + {$IFDEF MOBILE} + FSetToRecBookm: TBookmark; + {$ENDIF} + + procedure ClearBlobCache(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); + function GetActiveRecBuf(var RecBuf: TRecordBuffer): Boolean; + function GetBlobData(Field: TField; Buffer: TRecordBuffer): TBlobData; + function GetOldRecord: PAnsiDACChar; + procedure InitBufferPointers(GetProps: Boolean); + function RecordFilter(RecBuf: Pointer; RecNo: Integer): Smallint; stdcall; + procedure SetBlobData(Field: TField; Buffer: TRecordBuffer; Value: TBlobData); + function GetDBHandle: DAChDBIDb; + procedure SetUpdateMode(const Value: TUpdateMode); + procedure SetAutoRefresh(const Value: Boolean); + procedure SetDatabase(Value : TPSQLDatabase); + function GetDatabase:TPSQLDatabase; + {$IFNDEF DELPHI_4}{$IFNDEF FPC} + procedure SetupAutoRefresh; + {$ENDIF}{$ENDIF} + function GetStmtHandle: HDBIStmt; + procedure SetSortFieldNames(const Value: string); + function GetSortFieldNames: string; + procedure ReadByteaOpt(Reader: TReader); //deal with old missing properties + procedure ReadOIDOpt(Reader: TReader); //deal with old missing properties + function GetStoreActive: boolean; + + protected + procedure DefineProperties(Filer: TFiler); override; + { IProviderSupport } + {$IFNDEF FPC} + procedure PSEndTransaction(Commit: Boolean); override; +{$IFDEF DELPHI_17} + function PSExecuteStatement(const ASQL: string; AParams: TParams): Integer; override; + function PSExecuteStatement(const ASQL: string; AParams: TParams; + var ResultSet: TDataSet): Integer; override; +{$ELSE} + function PSExecuteStatement(const ASQL: string; AParams: TParams; + ResultSet: Pointer = nil): Integer; override; +{$ENDIF DELPHI_17} + procedure PSGetAttributes(List: {$IFDEF DELPHI_19}TPacketAttributeList{$ELSE}TList{$ENDIF}); override; + function PSGetQuoteChar: string; override; + function PSInTransaction: Boolean; override; + function PSIsSQLBased: Boolean; override; + function PSIsSQLSupported: Boolean; override; + procedure PSStartTransaction; override; + procedure PSReset; override; + function PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; override; + {$ENDIF} + protected + function Engine : TPSQLEngine; Virtual; Abstract; + {$IFNDEF FPC} + procedure SetBlockReadSize(Value: Integer); override; + procedure BlockReadNext; override; + {$ENDIF} + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure ActivateFilters; + procedure AddFieldDesc(FieldDescs: TFLDDescList; var DescNo: Integer; + var FieldID: Integer; RequiredFields: TBits; FieldDefs: TFieldDefs); + procedure AllocCachedUpdateBuffers(Allocate: Boolean); + procedure AllocKeyBuffers; +{$IFDEF NEXTGEN} + function AllocRecBuf: TRecBuf; override; + procedure FreeRecBuf(var Buffer: TRecBuf); override; +{$ELSE} + function AllocRecordBuffer: TRecordBuffer; override; + procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override; +{$ENDIF NEXTGEN} + function CachedUpdateCallBack(CBInfo: Pointer): CBRType; + procedure CheckCachedUpdateMode; + procedure CheckSetKeyMode; + procedure ClearCalcFields(Buffer:{$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; + procedure CloseCursor; override; +{$IFDEF DELPHI_12} + procedure CreateFields; override; +{$ENDIF DELPHI_12} + procedure CloseBlob(Field: TField); override; + function CreateExprFilter(const Expr: string; + Options: TFilterOptions; Priority: Integer): HDBIFilter; + function CreateFuncFilter(FilterFunc: Pointer; + Priority: Integer): HDBIFilter; + function CreateHandle: HDBICur; virtual; + function CreateLookupFilter(Fields: TList{$IFDEF NEXTGEN}{$ENDIF}; const Values: Variant; + Options: TLocateOptions; Priority: Integer): HDBIFilter; + procedure DataEvent(Event: TDataEvent; Info: TDataEventInfo); override; + procedure DeactivateFilters; + procedure DestroyHandle; virtual; + function FindRecord(Restart, GoForward: Boolean): Boolean; override; + function ForceUpdateCallback: Boolean; + procedure FreeKeyBuffers; +{$IFNDEF NEXTGEN} + procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override; +{$ENDIF} +{$IFDEF DELPHI_17} + procedure GetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); override; +{$ENDIF DELPHI_17} + function GetBookmarkFlag(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}): TBookmarkFlag; override; + function GetRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override; + procedure InitRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; + +{$IFDEF NEXTGEN} + procedure InternalGotoBookmark(Bookmark: TBookmark); override; +{$ELSE} + procedure InternalGotoBookmark(Bookmark: Pointer); override; +{$ENDIF} + + procedure InternalInitRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; + procedure InternalSetToRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); override; + function GetCanModify: Boolean; override; +{$IFNDEF FPC} + function GetFieldFullName(Field: TField): string; override; +{$ENDIF} + function GetFieldClass(FieldType: TFieldType): TFieldClass; override; +{$IFDEF DELPHI_12} + function GetFieldClass(FieldDef: TFieldDef): TFieldClass; override; + function GetFieldDefsClass: TFieldDefsClass; override; + function GetLookupListClass(Field: TField): TLookupListClass; override; +{$ENDIF} + function GetIndexField(Index: Integer): TField; + function GetIndexFieldCount: Integer; + function GetIsIndexField(Field: TField): Boolean; override; + function GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer; + function GetKeyExclusive: Boolean; + function GetKeyFieldCount: Integer; + function GetRecordCount: Integer; override; + function GetRecNo: Integer; override; + function GetRecordSize: integer; reintroduce; virtual; + {$IFNDEF FPC} + function GetStateFieldValue(State: TDataSetState; Field: TField): Variant; override; + procedure GetObjectTypeNames(Fields: TFields); + {$ENDIF} + function GetUpdatesPending: Boolean; + {$IFNDEF FPC} + function GetUpdateRecordSet: TUpdateRecordTypes; + {$ENDIF} + function InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer; + procedure InternalAddRecord(Buffer: {$IFNDEF NEXTGEN}Pointer{$ELSE}TRecBuf{$ENDIF}; Append: Boolean); override; + procedure InternalCancel; override; + procedure InternalClose; override; + procedure InternalDelete; override; + procedure InternalEdit; override; + procedure InternalFirst; override; + + procedure InternalHandleException; override; + procedure InternalInitFieldDefs; override; + + procedure InternalInsert; override; + procedure InternalLast; override; + procedure InternalOpen; override; + procedure InternalPost; override; + procedure InternalRefresh; override; + function IsCursorOpen: Boolean; override; + function LocateRecord(const KeyFields: string; const KeyValues: Variant; + Options: TLocateOptions; SyncCursor: Boolean): Boolean; + function LocateFilteredRecord(const KeyFields: string; + const KeyValues: Variant; + Options: TLocateOptions; + SyncCursor: Boolean): Word; + function LocateNearestRecord(const KeyFields: string; const KeyValues: Variant; + Options: TLocateOptions; SyncCursor: Boolean): Word; + procedure PostKeyBuffer(Commit: Boolean); + procedure PrepareCursor; Virtual; + function ProcessUpdates(UpdCmd: DBIDelayedUpdCmd): Word; + function ResetCursorRange: Boolean; + {$IFNDEF NEXTGEN} + procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override; + {$ENDIF} + {$IFDEF DELPHI_17} + procedure SetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); override; + {$ENDIF DELPHI_17} + procedure SetBookmarkFlag(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Value: TBookmarkFlag); override; + procedure SetCachedUpdates(Value: Boolean); + function SetCursorRange: Boolean; + {$IFNDEF NEXTGEN} + procedure SetFieldData(Field: TField; Buffer: Pointer); override; + {$ENDIF} + {$IFDEF DELPHI_17} + procedure SetFieldData(Field: TField; Buffer: TValueBuffer); override; + {$ENDIF} + procedure SetFilterData(const Text: string; Options: TFilterOptions); + procedure SetFilterHandle(var Filter: HDBIFilter; Value: HDBIFilter); + procedure SetFiltered(Value: Boolean); override; + procedure SetFilterOptions(Value: TFilterOptions); override; + procedure SetFilterText(const Value: string); override; + procedure SetIndexField(Index: Integer; Value: TField); + procedure SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean); + procedure SetKeyExclusive(Value: Boolean); + procedure SetKeyFieldCount(Value: Integer); + procedure SetKeyFields(KeyIndex: TKeyIndex; const Values: array of const); + procedure SetLinkRanges(MasterFields: TList{$IFDEF DELPHI_17}{$ENDIF}); + {$IFNDEF FPC} + procedure SetStateFieldValue(State: TDataSetState; Field: TField; const Value: Variant); override; + {$ENDIF} + procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override; + {$IFNDEF FPC} + procedure SetOnUpdateError(UpdateEvent: TUpdateErrorEvent); + {$ENDIF} + procedure SetOptions(const Value: TPSQLDatasetOptions); virtual; + procedure SetRecNo(Value: Integer); override; + procedure SetupCallBack(Value: Boolean); + {$IFNDEF FPC} + procedure SetUpdateRecordSet(RecordTypes: TUpdateRecordTypes); + {$ENDIF} + procedure SetUpdateObject(Value: TPSQLSQLUpdateObject); + procedure SwitchToIndex(const IndexName, TagName: string); + function UpdateCallbackRequired: Boolean; + procedure Disconnect; Virtual; + procedure OpenCursor(InfoQuery: Boolean); override; + function SetDBFlag(Flag: Integer; Value: Boolean): Boolean; virtual; + function GetHandle: HDBICur; + property DBFlags: TDBFlags read FDBFlags; + property UpdateMode: TUpdateMode read FUpdateMode write SetUpdateMode default upWhereAll; + property StmtHandle: HDBIStmt read GetStmtHandle; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + function GetLastInsertID(const FieldNum: integer): integer; + function GetFieldTypeOID(const FieldNum: integer): cardinal; + procedure ApplyUpdates; + function BookmarkValid(Bookmark: TBookmark): Boolean; override; + procedure Cancel; override; + procedure CancelUpdates; + property CacheBlobs: Boolean read FCacheBlobs write FCacheBlobs default TRUE; + function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override; + procedure CommitUpdates; + procedure FetchAll; + procedure FlushBuffers; + function GetCurrentRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}): Boolean; override; + {$IFNDEF FPC} + function GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer; override; + {$ENDIF} + {$IFNDEF NEXTGEN} + function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override; + function GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; {$IFNDEF FPC}override;{$ENDIF} + {$ENDIF} + {$IFDEF DELPHI_17} + function GetFieldData(FieldNo: Integer; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; override; + function GetFieldData(Field: TField; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; override; + {$ENDIF} + procedure GetIndexInfo; + function Locate(const KeyFields: string; const KeyValues: Variant; + Options: TLocateOptions): Boolean; override; + function Lookup(const KeyFields: string; const KeyValues: Variant; + const ResultFields: string): Variant; override; + function IsSequenced: Boolean; override; + procedure Post; override; + procedure RevertRecord; + function UpdateStatus: TUpdateStatus; override; + function CheckOpen(Status: Word): Boolean; + procedure CloseDatabase(Database: TPSQLDatabase); + procedure GetDatabaseNames(List: TStrings); + property DBHandle: DAChDBIDb read GetDBHandle; + property Handle: HDBICur read GetHandle; + property ExpIndex: Boolean read FExpIndex; + property KeySize: integer read FKeySize; + property UpdateObject: TPSQLSQLUpdateObject read FUpdateObject write SetUpdateObject; + property UpdatesPending: Boolean read GetUpdatesPending; + {$IFNDEF FPC} + property UpdateRecordTypes: TUpdateRecordTypes read GetUpdateRecordSet write SetUpdateRecordSet; + {$ENDIF} + procedure PopulateFieldsOrigin(); + procedure SortBy(FieldNames : string; Compare : TPSQLDatasetSortCompare); overload; + procedure SortBy(FieldNames : string); overload; + property SortFieldNames : string read GetSortFieldNames write SetSortFieldNames; + property RecordSize: integer read GetRecordSize; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property AutoRefresh: Boolean read FAutoRefresh write SetAutoRefresh default FALSE; + property Database: TPSQLDatabase read GetDatabase write SetDatabase; + property CachedUpdates: Boolean read FCachedUpdates write SetCachedUpdates default False; + property AllowSequenced : Boolean read FAllowSequenced Write FAllowSequenced default False; //Added by Nicolas Ring + property Filter; + property Filtered; + property FilterOptions; + property OnFilterRecord; + property Active stored GetStoreActive; + property AutoCalcFields; + {$IFNDEF FPC} + property ObjectView default FALSE; + {$ENDIF} + property Options: TPSQLDatasetOptions read FOptions write SetOptions; + property BeforeOpen; + property AfterOpen; + property BeforeClose; + property AfterClose; + property BeforeInsert; + property AfterInsert; + property BeforeEdit; + property AfterEdit; + property BeforePost; + property AfterPost; + property BeforeCancel; + property AfterCancel; + property BeforeDelete; + property AfterDelete; + property BeforeScroll; + property AfterScroll; + property BeforeRefresh; + property AfterRefresh; + property OnCalcFields; + property OnDeleteError; + property OnEditError; + property OnNewRecord; + property OnPostError; + {$IFNDEF FPC} + property OnUpdateError: TUpdateErrorEvent read FOnUpdateError write SetOnUpdateError; + property OnUpdateRecord: TUpdateRecordEvent read FOnUpdateRecord write FOnUpdateRecord; + {$ENDIF} + end; + +////////////////////////////////////////////////////////// +//Class : TPSQLTable +//Description : TPSQLTable class +////////////////////////////////////////////////////////// + TTableType = (ttDefault, ttParadox, ttDBase, ttFoxPro, ttASCII); + TIndexName = type string; + + TValCheckList = array of VCHKDesc; + + TPSQLTable = Class(TPSQLDataSet) + Private + FStoreDefs: Boolean; + FIndexDefs: TIndexDefs; + FMasterLink: TMasterDataLink; + FDefaultIndex: Boolean; + FExclusive: Boolean; + FReadOnly: Boolean; + FFieldsIndex: Boolean; + FTableName: TFileName; + FIndexName: TIndexName; + FTableLevel: Integer; + FNativeTableName: DBITBLNAME; + FLimit : Integer; + FOffset: Integer; + FShowSystemTables: boolean; + FTableID: cardinal; + FComment: string; + FOwner: string; + FTablespace: string; + procedure SetLimit(const Value : Integer); + function GetLimit: integer; + procedure CheckMasterRange; + procedure DecodeIndexDesc(const IndexDesc: IDXDesc; + var Source, Name, FieldExpression, DescFields: string; + var Options: TIndexOptions); + function FieldDefsStored: Boolean; + function GetExists: Boolean; + function GetIndexFieldNames: string; + function GetIndexName: string; + procedure GetIndexParams(const IndexName: string; FieldsIndex: Boolean; + var IndexedName, IndexTag: string); + function GetMasterFields: string; + function GetTableLevel: Integer; + function IndexDefsStored: Boolean; + procedure MasterChanged(Sender: TObject); + procedure MasterDisabled(Sender: TObject); + procedure SetDataSource(Value: TDataSource); + procedure SetExclusive(Value: Boolean); + procedure SetIndexDefs(Value: TIndexDefs); + procedure SetIndex(const Value: string; FieldsIndex: Boolean); + procedure SetIndexFieldNames(const Value: string); + procedure SetIndexName(const Value: string); + procedure SetMasterFields(const Value: string); + procedure SetReadOnly(Value: Boolean); + procedure SetTableName(const Value: TFileName); + function GetTableName: TFileName; + procedure UpdateRange; + function GetBatchModify: Boolean; + procedure SetBatchModify(const Value : Boolean); + procedure SetShowSystemTables(const Value: boolean); + function GetOffset: Integer; + procedure SetOffset(const Value: Integer); + procedure SetDummyInt(const Value: cardinal); + procedure SetDummyStr(const Value: string); + function GetTableSpace: string; + Protected + { IProviderSupport } + {$IFNDEF FPC} + function PSGetDefaultOrder: TIndexDef; override; + function PSGetKeyFields: string; override; + function PSGetTableName: string; override; + function PSGetIndexDefs(IndexTypes: TIndexOptions): TIndexDefs; override; + procedure PSSetCommandText(const CommandText: string); override; + procedure PSSetParams(AParams: TParams); override; + {$ENDIF} + function CreateHandle: HDBICur; override; + procedure DataEvent(Event: TDataEvent; Info: TDataEventInfo); override; + {$IFNDEF FPC} + procedure DefChanged(Sender: TObject); override; + {$ENDIF} + procedure DestroyHandle; override; + procedure DoOnNewRecord; override; + procedure EncodeFieldDesc(var FieldDesc: FLDDesc; + const Name: string; DataType: TFieldType; Size, Precision: Integer); + procedure EncodeIndexDesc(var IndexDesc: IDXDesc; + const Name, FieldExpression: string; Options: TIndexOptions; + const DescFields: string = ''); + function GetCanModify: Boolean; override; + function GetDataSource: TDataSource; override; + function GetHandle(const IndexName, IndexTag: string): HDBICur; + function GetLanguageDriverName: string; + procedure InitFieldDefs; override; + function GetFileName: string; + function GetTableType: TTableType; + function NativeTableName: PAnsiDACChar; + procedure PrepareCursor; override; + procedure UpdateIndexDefs; override; + procedure SetOptions(const Value: TPSQLDatasetOptions); override; + property MasterLink: TMasterDataLink read FMasterLink; + Public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + function Engine : TPSQLEngine; override; + function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override; + function IsSequenced: Boolean; override; + procedure AddIndex(const Name, Fields: string; Options: TIndexOptions; const DescFields: string = ''); + procedure ApplyRange; + procedure CancelRange; + procedure CreateTable; + procedure DeleteIndex(const Name: string); + procedure EditKey; + procedure EditRangeEnd; + procedure EditRangeStart; + procedure EmptyTable; + function FindKey(const KeyValues: array of const): Boolean; + procedure FindNearest(const KeyValues: array of const); + {$IFNDEF FPC} + procedure GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF DELPHI_17}{$ENDIF}); override; + {$ENDIF} + procedure GetIndexNames(List: TStrings); + procedure GotoCurrent(Table: TPSQLTable); + function GotoKey: Boolean; + procedure GotoNearest; + Procedure LockTable(LockType: TPSQLLockType; NoWait: boolean); + procedure SetKey; + procedure SetRange(const StartValues, EndValues: array of const); + procedure SetRangeEnd; + procedure SetRangeStart; + property Exists: Boolean read GetExists; + property IndexFieldCount: Integer read GetIndexFieldCount; + property IndexFields[Index: Integer]: TField read GetIndexField write SetIndexField; + property KeyExclusive: Boolean read GetKeyExclusive write SetKeyExclusive; + property KeyFieldCount: Integer read GetKeyFieldCount write SetKeyFieldCount; + property TableLevel: Integer read GetTableLevel write FTableLevel; + property BatchModify : Boolean read GetBatchModify write SetBatchModify default False; + published + property DefaultIndex: Boolean read FDefaultIndex write FDefaultIndex default TRUE; + property Exclusive: Boolean read FExclusive write SetExclusive default FALSE; + property FieldDefs stored FieldDefsStored; + property IndexDefs: TIndexDefs read FIndexDefs write SetIndexDefs stored IndexDefsStored; + property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames; + property IndexName: string read GetIndexName write SetIndexName; + property MasterFields: string read GetMasterFields write SetMasterFields; + property MasterSource: TDataSource read GetDataSource write SetDataSource; + property ReadOnly: Boolean read FReadOnly write SetReadOnly default FALSE; + property StoreDefs: Boolean read FStoreDefs write FStoreDefs default FALSE; + property ShowSystemTables: boolean read FShowSystemTables write SetShowSystemTables default False; + property TableName: TFileName read GetTableName write SetTableName; + property UpdateMode; + property UpdateObject; + property Limit : Integer read GetLimit write SetLimit default 0; + property Offset : Integer read GetOffset write SetOffset default 0; + property Owner: string read FOwner write SetDummyStr stored False; + property Tablespace: string read GetTableSpace write SetDummyStr stored False; + property TableID: cardinal read FTableID write SetDummyInt stored False; + property Comment: string read FComment write SetDummyStr stored False; + property SortFieldNames; + end; + + +////////////////////////////////////////////////////////// +//Class : TPSQLQuery +//Description : Component TPSQLQuery +////////////////////////////////////////////////////////// + TPSQLQuery = Class(TPSQLDataSet) + Private + FSQL: TStrings; + FPrepared: Boolean; + FParams: TPSQLParams; + FText: string; + FDataLink: TDataLink; + FLocal: Boolean; + FRowsAffected: Integer; + FUniDirectional: Boolean; + FRequestLive: Boolean; + FSQLBinary: PChar; + FParamCheck: Boolean; + FExecSQL: Boolean; + FCheckRowsAffected: Boolean; + FBeforeExecSQL: TDataSetNotifyEvent; + function CreateCursor(GenHandle: Boolean): HDBICur; + function GetQueryCursor(GenHandle: Boolean): HDBICur; + function GetRowsAffected: Integer; + procedure PrepareSQL(Value: PChar); + procedure QueryChanged(Sender: TObject); + procedure ReadBinaryData(Stream: TStream); + procedure ReadParamData(Reader: TReader); + procedure RefreshParams; + procedure SetDataSource(Value: TDataSource); + procedure SetQuery(Value: TStrings); + function GetQuery:TStrings; + procedure SetParamsList(Value: TPSQLParams); + procedure SetParamsFromCursor; + procedure SetPrepared(Value: Boolean); + procedure SetPrepare(Value: Boolean); + procedure WriteBinaryData(Stream: TStream); + procedure WriteParamData(Writer: TWriter); + procedure SetRequestLive(const Value : Boolean); + function GetRequestLive : Boolean; + protected + { IProviderSupport } + {$IFNDEF FPC} + procedure PSExecute; override; + function PSGetDefaultOrder: TIndexDef; override; + function PSGetParams: TParams; override; + function PSGetTableName: string; override; + procedure PSSetCommandText(const CommandText: string); override; + procedure PSSetParams(AParams: TParams); override; + {$ENDIF} + function CreateHandle: HDBICur; override; + procedure DefineProperties(Filer: TFiler); override; + procedure Disconnect; override; + function GetDataSource: TDataSource; override; + function GetParamsCount: integer; + function SetDBFlag(Flag: Integer; Value: Boolean): Boolean; override; + procedure SetOptions(const Value: TPSQLDatasetOptions); override; + procedure GetStatementHandle(SQLText: PChar); virtual; + property DataLink: TDataLink read FDataLink; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + function Engine : TPSQLEngine; override; + function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override; + function IsSequenced: Boolean; override; + procedure ExecSQL; + {$IFNDEF FPC} + procedure GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF NEXTGEN}{$ENDIF}); override; + {$ENDIF} + function ParamByName(const Value: string): TPSQLParam; + procedure Prepare; + procedure UnPrepare; + property Prepared: Boolean read FPrepared write SetPrepare; + property ParamCount: integer read GetParamsCount; + property Local: Boolean read FLocal; + property Text: string read FText; + property RowsAffected: Integer read GetRowsAffected; + property SQLBinary: PChar read FSQLBinary write FSQLBinary; + published + property BeforeExecSQL: TDataSetNotifyEvent read FBeforeExecSQL write FBeforeExecSQL; + property DataSource: TDataSource read GetDataSource write SetDataSource; + property ParamCheck: Boolean read FParamCheck write FParamCheck default TRUE; + property RequestLive: Boolean read GetRequestLive write SetRequestLive default FALSE; + property SQL: TStrings read GetQuery write SetQuery; + property Params: TPSQLParams read FParams write SetParamsList stored FALSE; + property UniDirectional: Boolean read FUniDirectional write FUniDirectional default FALSE; + property UpdateMode; + property UpdateObject; + property SortFieldNames; + end; + + + TRecordChangeCompleteEvent = procedure(DataSet: TPSQLDataSet; const Reason: TUpdateKind) of object; + + { TPSQLUpdateSQL } + TPSQLUpdateSQL = Class(TPSQLSQLUpdateObject) + private + FAbout : TPSQLDACAbout; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDataSet: TPSQLDataSet; + FQueries: array[TUpdateKind] of TPSQLQuery; + FSQLText: array[TUpdateKind] of TStrings; + FRecordChangeCompleteEvent: TRecordChangeCompleteEvent; + function GetQuery(UpdateKind: TUpdateKind): TPSQLQuery; + function GetSQLIndex(Index: Integer): TStrings; + procedure SetSQL(UpdateKind: TUpdateKind; Value: TStrings); + procedure SetSQLIndex(Index: Integer; Value: TStrings); + protected + function GetSQL(UpdateKind: TUpdateKind): TStrings; override; + function GetQueryClass : TPSQLQueryClass; + function GetDataSet: TPSQLDataSet; override; + procedure SetDataSet(ADataSet: TPSQLDataSet); override; + procedure SQLChanged(Sender: TObject); + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure Apply(UpdateKind: TUpdateKind); override; + procedure ExecSQL(UpdateKind: TUpdateKind); + procedure SetParams(UpdateKind: TUpdateKind); + property DataSet; + property Query[UpdateKind: TUpdateKind]: TPSQLQuery read GetQuery; + property SQL[UpdateKind: TUpdateKind]: TStrings read GetSQL write SetSQL; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property ModifySQL: TStrings index 0 read GetSQLIndex write SetSQLIndex; + property InsertSQL: TStrings index 1 read GetSQLIndex write SetSQLIndex; + property DeleteSQL: TStrings index 2 read GetSQLIndex write SetSQLIndex; + property OnRecordChangeComplete: TRecordChangeCompleteEvent read FRecordChangeCompleteEvent write FRecordChangeCompleteEvent; + end; + + { TPSQLBlobStream } + TPSQLBlobStream = class(TStream) + private + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FField: TBlobField; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDataSet: TPSQLDataSet; + FBuffer: TRecordBuffer; + FMode: TBlobStreamMode; + FFieldNo: Integer; + FOpened: Boolean; + FModified: Boolean; + FPosition: Longint; + FBlobData: TBlobData; + FCached: Boolean; + FCacheSize: Longint; + function GetBlobSize: integer; + public + constructor Create(Field: TBlobField; Mode: TBlobStreamMode); + destructor Destroy; override; + function Engine : TPSQLEngine; + function PositionDataset: Boolean; + {$IFDEF DELPHI_17} + function Read(Buffer: TBytes; Offset, Count: Longint): Longint; override; + {$ENDIF DELPHI_17} + function Read(var Buffer; Count: Longint): Longint; override; + function Write(const Buffer; Count: Longint): Longint; override; + function Seek(Offset: Longint; Origin: Word): Longint; override; + procedure Truncate; + end; + + + TParamBindMode = (pbByName, pbByNumber); + + TSPParamDescList = array of SPParamDesc; + + TPSQLStoredProc = class(TPSQLDataSet) + private + FParams: TPSQLParams; + FNeedRefreshParams : boolean; + FOverload: cardinal; + FProcName: string; + FBindMode: TParamBindMode; + function GetParamsCount: integer; + procedure SetOverload(const Value: cardinal); + procedure SetProcName(const Value: string); + protected + { IProviderSupport } + {$IFNDEF FPC} + procedure PSExecute; override; + function PSGetTableName: string; override; + function PSGetParams: TParams; override; + procedure PSSetCommandText(const CommandText: string); override; + procedure PSSetParams(AParams: TParams); override; + {$ENDIF} + function CreateHandle: HDBICur;override; + function CreateCursor(IsExecProc : boolean): HDBICur; + procedure DataEvent(Event: TDataEvent; Info: TDataEventInfo); override; + procedure CloseCursor; override; + procedure SetProcedureName(const Value: string); + function GetParamsList: TPSQLParams; + procedure SetParamsList(const Value: TPSQLParams); + function GetCanModify: Boolean; override; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + + function CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; override; + function Engine : TPSQLEngine; override; + function DescriptionsAvailable: Boolean; + function ParamByName(const Value: string): TPSQLParam; + + procedure ExecProc; + procedure RefreshParams; + procedure SetNeedRefreshParams; + + property ParamsCount : integer read GetParamsCount; + published + property StoredProcName: string read FProcName write SetProcName; + property Overload: cardinal read FOverload write SetOverload default 0; + property Params: TPSQLParams read GetParamsList write SetParamsList; + property ParamBindMode: TParamBindMode read FBindMode write FBindMode default pbByName; + end; + +procedure Check(Engine : TPSQLEngine; Status: Word); +procedure NoticeProcessor(arg: Pointer; mes: PAnsiDACChar); cdecl; + +var + DBList : TList{$IFDEF NEXTGEN}{$ENDIF}; + +implementation + +uses + {$IFDEF DELPHI_10}DBClient, {$ENDIF} + PSQLDirectQuery, Math, PSQLFields, PSQLNotify + {$IFDEF NEXTGEN}, System.Character{$ENDIF}; + +//NoticeProcessor callback function +procedure NoticeProcessor(arg: Pointer; mes: PAnsiDACChar); +var s:string; +begin + if not Assigned(TPSQLDatabase(Arg).FOnNotice) then Exit; + if TPSQLDatabase(Arg).IsUnicodeUsed then + S := UTF8ToString(Mes) + else + S := string(Mes); + TPSQLDatabase(Arg).FOnNotice(TPSQLDatabase(Arg), S); +end; + +{ TPSQLQueryDataLink } +type + + TPSQLQueryDataLink = Class(TDetailDataLink) + private + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FQuery: TPSQLQuery; + protected + procedure ActiveChanged; override; + procedure RecordChanged(Field: TField); override; + function GetDetailDataSet: TDataSet; override; + procedure CheckBrowseMode; override; + public + constructor Create(AQuery: TPSQLQuery); + end; + +procedure TDbiError(Engine : TPSQLEngine; ErrorCode: Word); +begin + Raise EPSQLDatabaseError.Create(Engine, ErrorCode); +end; + +procedure Check(Engine : TPSQLEngine; Status: Word); +begin + if Status <> 0 then TDbiError(Engine, Status); +end; + +function GetIntProp(Engine : TPSQLEngine; const Handle: Pointer; PropName: Integer): Integer; +Var + Length : integer; + Value : Integer; +begin + Value := 0; + if (Engine.GetEngProp(HDBIObj(Handle), PropName, @Value, SizeOf(Value), Length) = DBIERR_NONE) then + Result := Value else + Result := 0; +end; + +function SetBoolProp(Engine : TPSQLEngine; const Handle: Pointer; PropName: Integer; Value: Boolean) : Boolean; +begin + Result := Engine.SetEngProp(HDBIObj(Handle), PropName, Abs(Integer(Value))) = DBIERR_NONE; +end; + +{ EPSQLDatabaseError } +constructor EPSQLDatabaseError.Create(Engine : TPSQLEngine; ErrorCode : Word); + + function GetErrorString: string; + var + Msg1 : string; + Msg2 : string; + Err : Integer; + begin + Msg1 := Engine.MessageStatus; + Err := Engine.Status; + if (Msg1 <> '') and (Err > 0) then + Msg1 := Format('PostgreSQL Error Code: (%s)'#13#10'%s', [FErrorsqlstate, Msg1]) + else + begin + Msg2 := GetBDEErrorMessage(ErrorCode); + Msg1 := Format('DBI Error Code: (%d)'#13#10'%s '#13#10'%s', [ErrorCode, Msg1, Msg2]); + end; + Result := Msg1 + end; + +var + NC: TNativeConnect; +begin + inherited Create(GetErrorString()); + FErrorCode := ErrorCode; + if Assigned(Engine.Database) then + begin + NC := TNativeConnect(Engine.Database); + FErrorPos := NC.FErrorPos; + FErrorContext := NC.FErrorContext; + FErrorseverity := NC.FErrorSeverity; + FErrorsqlstate := NC.FErrorsqlstate; + FErrorprimary := NC.FErrorprimary; + FErrordetail := NC.FErrordetail; + FErrorhint := NC.FErrorhint; + FErrorinternalpos := NC.FErrorinternalpos; + FErrorinternalquery := NC.FErrorinternalquery; + FErrorsourcefile := NC.FErrorsourcefile; + FErrorsourceline := NC.FErrorsourceline; + FErrorsourcefunc := NC.FErrorsourcefunc; + FErrorSchemaName := NC.FErrorSchemaName; + FErrorTableName := NC.FErrorTableName; + FErrorColumnName := NC.FErrorColumnName; + FErrorDataTypeName := NC.FErrorDataTypeName; + FErrorConstraintName := NC.FErrorConstraintName; + end; + if Message = EmptyStr then + Message := Format('PSQLDAC Interface Error: (%d)',[ErrorCode]); +end; + +destructor EPSQLDatabaseError.Destroy; +begin + Inherited Destroy; +end; + + +{ TPSQLDatabase } +procedure TPSQLDatabase.InitEngine; +begin + try + if FEngine = nil then FEngine := TPSQLEngine.Create(nil, nil); + except + raise EDatabaseError.Create('The Engine is not initialized'); + end; +end; + +procedure TPSQLDatabase.AddDatabase(Value : TPSQLDatabase); +begin + DBList.Add(Value); +end; + +procedure TPSQLDatabase.RemoveDatabase(Value : TPSQLDatabase); +begin + while DataSetCount <> 0 do + TPSQLDataSet(DataSets[DataSetCount - 1]).FDatabase := nil; + + if Assigned(DBList) then + DBList.Remove(Value); + + while FDirectQueryList.Count > 0 do + TPSQLDirectQuery(FDirectQueryList[FDirectQueryList.Count - 1]).Database := nil; +end; + +procedure TPSQLDatabase.WriteState(Writer: TWriter); +var OldPwd: string; +begin + if not (ddoStorePassword in FDesignOptions) then + begin + OldPwd := GetUserPassword(); + SetUserPassword(''); + end; + + inherited; + + if not (ddoStorePassword in FDesignOptions) then + SetUserPassword(OldPwd); +end; + +constructor TPSQLDatabase.Create(AOwner : TComponent); +begin + Inherited Create(AOwner); + FParams := TStringList.Create; + TStringList(FParams).OnChanging := ParamsChanging; + FKeepConnection := TRUE; + FOEMConvert := False; + SetConnectionTimeout(15); + SetServerPort(PSQL_PORT); + SetSSLMode(sslPrefer); + FTransIsolation := tiReadCommitted; + AddDatabase(Self); + FNotifyList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + FDirectQueryList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + FCheckIfActiveOnParamChange := True; //SSH Tunneling stuff + SetConnectionTimeout(15); + FDatabaseID := InvalidOid; + FDesignOptions := [ddoStoreConnected, ddoStorePassword]; + FErrorVerbosity := evDEFAULT; +end; + +destructor TPSQLDatabase.Destroy; +begin + Destroying; + Close; + RemoveDatabase(Self); + FEngine.Free; + FNotifyList.Free; + FDirectQueryList.Free; + FParams.Free; + FStmtList.Free; + inherited Destroy; +end; + + +procedure TPSQLDatabase.ApplyUpdates(const DataSets: array of TPSQLDataSet); +var + I : Integer; + DS : TPSQLDataSet; +begin + StartTransaction; + try + for I := 0 to High(DataSets) do + begin + DS := DataSets[I]; + if (DS.Database <> Self) then + DatabaseError(Format(SUpdateWrongDB, [DS.Name, Name])); + DataSets[I].ApplyUpdates; + end; + Commit; + except + Rollback; + raise; + end; + for I := 0 to High(DataSets) do DataSets[I].CommitUpdates; +end; + +procedure TPSQLDatabase.AssignTo(Dest: TPersistent); +var DestDB: TPSQLDatabase; +begin + if not (Dest is TPSQLDatabase) then + inherited AssignTo(Dest) + else + begin + DestDB := TPSQLDatabase(Dest); + DestDB.Params.Assign(Self.Params); + end; +end; + +type + PStmtInfo = ^TStmtInfo; + TStmtInfo = packed record + HashCode: Integer; + StmtHandle: HDBIStmt; + SQLText: string; + end; + +procedure TPSQLDatabase.ClearStatements; +var + i: Integer; +begin + if Assigned(FStmtList) then + begin + for i := 0 to FStmtList.Count - 1 do + begin + Engine.QFree(PStmtInfo(FStmtList[i]).StmtHandle); + Dispose(PStmtInfo(FStmtList[i])); + end; + FStmtList.Clear; + end; +end; + +function TPSQLDatabase.Execute(const SQL: string; Params: TParams = nil; + Cache: Boolean = FALSE; Cursor: phDBICur = nil): Integer; + + function GetStmtInfo(SQL: PChar): PStmtInfo; + + function GetHashCode(Str: PChar): Integer; + var + Off, Len, Skip, I: Integer; + begin + Result := 0; + Off := 1; + Len := StrLen(Str); + if Len < 16 then + for I := (Len - 1) downto 0 do + begin + Result := (Result * 37) + Ord(Str[Off]); + Inc(Off); + end + else + begin + { Only sample some characters } + Skip := Len div 8; + I := Len - 1; + while I >= 0 do + begin + Result := (Result * 39) + Ord(Str[Off]); + Dec(I, Skip); + Inc(Off, Skip); + end; + end; + end; + + var + HashCode, i: Integer; + Info: PStmtInfo; + begin + if not Assigned(FStmtList) then + FStmtList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + Result := nil; + HashCode := GetHashCode(SQL); + for i := 0 to FStmtList.Count - 1 do + begin + Info := PStmtInfo(FStmtList[i]); + if (Info.HashCode = HashCode) and + AnsiSameText(PChar(Info.SQLText), SQL) then + begin + Result := Info; + break; + end; + end; + if not Assigned(Result) then + begin + New(Result); + FStmtList.Add(Result); + FillChar(Result^, SizeOf(Result^), 0); + Result.HashCode := HashCode; + end; + end; + + function GetStatementHandle: HDBIStmt; + var + Info: PStmtInfo; + Status: Word; + begin + Info := nil; + Result := nil; + if Cache then + begin + Info := GetStmtInfo(PChar(SQL)); + Result := Info.StmtHandle; + end; + if not Assigned(Result) then + begin + Check(Engine, Engine.QAlloc(Handle, Result)); + if Cursor <> nil then + Check(Engine, Engine.SetEngProp(hDbiObj(Result), stmtLIVENESS, Ord(wantCanned))); + SetBoolProp(Engine, Result, stmtUNIDIRECTIONAL, TRUE); + while TRUE do + begin + Status := Engine.QPrepare(Result, SQL); + case Status of + DBIERR_NONE: break; + DBIERR_NOTSUFFTABLERIGHTS: TDbiError(Engine, Status); + end; + end; + if Assigned(Info) then + begin + Info.SQLText := SQL; + Info.StmtHandle := Result; + end; + end; + end; + +var + StmtHandle : HDBIStmt; + Len : integer; +begin + StmtHandle := nil; + Result := 0; + Open; + if Assigned(Params) and (Params.Count > 0) then + begin + StmtHandle := GetStatementHandle; + try + Check(Engine, Engine.QuerySetParams(StmtHandle, Params, SQL)); + Check(Engine, Engine.QExec(StmtHandle, Cursor, Result)); + finally + if not Cache then Engine.QFree(StmtHandle); + end; + end + else + Check(Engine, Engine.QExecDirect(Handle, SQL, Cursor, Result)); + if Result = 0 then + if (Cursor = nil) and (Engine.GetEngProp(hDBIObj(StmtHandle), stmtROWCOUNT,@Result, SizeOf(Result), Len) <> 0) then + Result := 0; +end; + +procedure TPSQLDatabase.CheckActive; +begin + if FHandle = nil then DatabaseError(SDatabaseClosed); +end; + +procedure TPSQLDatabase.CheckInactive; +begin + if FHandle <> nil then + if csDesigning in ComponentState then + Close else + DatabaseError(SDatabaseOpen, Self); +end; + +procedure TPSQLDatabase.CloseDatabaseHandle; +begin + Engine.CloseDatabase(FHandle); +end; + +procedure TPSQLDatabase.CloseDatabase(Database: TPSQLDatabase); +begin + with Database do + begin + if FRefCount <> 0 then Dec(FRefCount); + if (FRefCount = 0) and not KeepConnection then Close; + end; +end; + +procedure TPSQLDatabase.DefineProperties(Filer: TFiler); +begin + inherited; + Filer.DefineProperty('UseSSL', SetUseSSL, nil, False); //missing +end; + +function TPSQLDatabase.GetSSLOption(Index: integer): string; +begin + Result := FParams.Values[SSLOpts[Index]]; +end; + +procedure TPSQLDatabase.SetSSLOption(Index: integer; Value: string); +begin + FParams.Values[SSLOpts[Index]] := Value; +end; + +procedure TPSQLDatabase.DoDisconnect; +begin + if FHandle <> nil then + begin + ClearStatements; + CloseDataSets; + if not FAcquiredHandle then + CloseDatabaseHandle else + FAcquiredHandle := FALSE; + FHandle := nil; + FRefCount := 0; + FDatabaseID := 0; + FIsTemplate := False; + FTablespace := ''; + FComment := ''; + FServerVersion := ''; + FOwner := ''; + end; +end; + +procedure TPSQLDatabase.CloseDataSets; +begin + while DataSetCount <> 0 do + TPSQLDataSet(DataSets[DataSetCount - 1]).Disconnect; +end; + +procedure TPSQLDatabase.Commit; +begin + CheckActive; + EndTransaction(xendCOMMIT); +end; + +procedure TPSQLDatabase.Rollback; +begin + CheckActive; + EndTransaction(xendABORT); +end; + +procedure TPSQLDatabase.StartTransaction; +var + TransHandle: HDBIXAct; +begin + CheckActive; + Check(Engine, Engine.BeginTran(FHandle, EXILType(FTransIsolation),TransHandle)); +end; + +procedure TPSQLDatabase.EndTransaction(TransEnd : EXEnd); +begin + Check(Engine, Engine.EndTran(FHandle, nil, TransEnd)); +end; + +procedure TPSQLDatabase.Savepoint(const Name: string); +begin + CheckActive(); + Execute('SAVEPOINT ' + Name); +end; + +procedure TPSQLDatabase.ReleaseSavepoint(const Name: string); +begin + CheckActive(); + Execute('RELEASE SAVEPOINT ' + Name); +end; + +procedure TPSQLDatabase.ReloadGUC; +begin + if Assigned(FHandle) then + TNativeConnect(FHandle).ReloadGUC(); +end; + +procedure TPSQLDatabase.RollbackToSavepoint(const Name: string); +begin + CheckActive(); + Execute('ROLLBACK TO SAVEPOINT ' + Name); +end; + +function TPSQLDatabase.GetConnected: Boolean; +begin + Result := FHandle <> nil; +end; + +function TPSQLDatabase.GetConnectionTimeout: cardinal; +begin + Result := StrToUIntDef(FParams.Values['connect_timeout'], 15); +end; + +function TPSQLDatabase.GetStoreConnected: Boolean; +begin + Result := Connected and + (ddoStoreConnected in FDesignOptions); +end; + +function TPSQLDatabase.GetDataSet(Index : Integer) : TPSQLDataSet; +begin + Result := inherited GetDataSet(Index) as TPSQLDataSet; +end; + +procedure TPSQLDatabase.SetDatabaseFlags; +var + Length: integer; + Buffer: DBINAME; +begin + Check(Engine, Engine.GetEngProp(HDBIOBJ(FHandle), dbDATABASETYPE, @Buffer, SizeOf(Buffer), Length)); + FPseudoIndexes := FALSE; +end; + +function TPSQLDatabase.GetInTransaction: Boolean; +var + TranInfo : XInfo; +begin + Result := (Handle <> nil) and + (Engine.GetTranInfo(Handle, nil, @TranInfo) = DBIERR_NONE) and + ( (TranInfo.exState = xsActive)); +end; + +function TPSQLDatabase.GetServerPort: Cardinal; +begin + Result := StrToUIntDef(FParams.Values['port'], PSQL_PORT); +end; + +function TPSQLDatabase.GetServerVersionAsInt: integer; +begin + if Assigned(Handle) then + Result := TNativeConnect(Handle).GetserverVersionAsInt + else + Result := 0; +end; + +function TPSQLDatabase.GetTransactionStatus: TTransactionStatusType; +begin + if Handle <> nil then + Engine.GetTranStatus(Handle,Result) + else + Result := trstUnknown; +end; + +procedure TPSQLDatabase.Loaded; + + procedure ChangeOldParameter(const OldName, NewName: string); + var I: integer; + V: string; + begin + I := FParams.IndexOfName(OldName); + if I = -1 then Exit; + V := Copy(FParams[I], Length(OldName) + 2, MaxInt); + FParams.Delete(I); + FParams.Values[NewName] := V; + end; + +begin + inherited Loaded; + if not StreamedConnected then InitEngine; + TStringList(FParams).OnChanging := nil; + try + ChangeOldParameter('UID', 'user'); + ChangeOldParameter('PWD', 'password'); + ChangeOldParameter('DatabaseName', 'dbname'); + ChangeOldParameter('ConnectionTimeout', 'connect_timeout'); + ChangeOldParameter('Port', 'port'); + ChangeOldParameter('SSLMode', 'sslmode'); + ChangeOldParameter('Host', 'host'); + if IsValidIP(FParams.Values['host']) then + begin + FParams.Values['hostaddr'] := FParams.Values['host']; + FParams.Values['host'] := ''; + end; + finally + TStringList(FParams).OnChanging := ParamsChanging; + end; +end; + +procedure TPSQLDatabase.Notification(AComponent : TComponent; Operation : TOperation); +begin + inherited Notification(AComponent, Operation); +end; + +procedure TPSQLDatabase.Login(LoginParams: TStrings); +begin + if Assigned(FOnLogin) then + FOnLogin(Self, LoginParams) + else + DatabaseErrorFmt(SLoginPrompt, [DatabaseName]); +end; + +procedure TPSQLDatabase.DoConnect; +const + OpenModes: array[Boolean] of DbiOpenMode = (dbiReadWrite, dbiReadOnly); + ShareModes: array[Boolean] of DbiShareMode = (dbiOpenShared, dbiOpenExcl); +var + s: String; +begin + if FHandle = nil then + begin + InitEngine; + if LoginPrompt then Login(FParams); + OEMConv := FOEMConvert; + s:= FParams.Text; + Check(Engine, Engine.OpenDatabase(FParams, FUseSingleLineConnInfo, FHandle)); + Check(Engine, Engine.GetServerVersion(FHandle, FServerVersion)); + Check(Engine, Engine.SetCharacterSet(FHandle, FCharSet)); + Check(Engine, Engine.SetCommandTimeout(FHandle, FCommandTimeout)); + if FErrorVerbosity <> evDEFAULT then + Check(Engine, Engine.SetErrorVerbosity(FHandle, FErrorVerbosity)); + if Assigned(FHandle) then + PQSetNoticeProcessor(TNativeConnect(FHandle).Handle, NoticeProcessor, Self); + SetBoolProp(Engine, FHandle, dbUSESCHEMAFILE, TRUE); + SetBoolProp(Engine, FHandle, dbPARAMFMTQMARK, TRUE); + SetBoolProp(Engine, FHandle, dbCOMPRESSARRAYFLDDESC, TRUE); + SetDatabaseFlags; + end; +end; + +procedure TPSQLDatabase.ParamsChanging(Sender: TObject); +begin + if FCheckIfActiveOnParamChange then CheckInactive; //SSH tunneling +end; + +function TPSQLDatabase.Ping(ConnectionParams: TStrings): TPingStatus; +begin + Check(Engine, Engine.Ping(ConnectionParams, Result)); +end; + +function TPSQLDatabase.Ping: TPingStatus; +begin + Check(Engine, Engine.Ping(FParams, Result)); +end; + +procedure TPSQLDatabase.SetDatabaseName(const Value : string); +begin + if not (csReading in ComponentState) then + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FParams.Values['dbname'] := Value; +end; + +procedure TPSQLDatabase.SetServerPort(const Value : Cardinal); +begin + if not (csReading in ComponentState) then + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FParams.Values['port'] := UIntToStr(Value); +end; + +procedure TPSQLDatabase.SetConnectionTimeout(const Value : Cardinal); +begin + if not (csReading in ComponentState) then + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FParams.Values['connect_timeout'] := IntToStr(Value); +end; + +procedure TPSQLDatabase.SetCommandTimeout(const Value : Cardinal); +begin + if FCommandTimeout <> Value then + begin + FCommandTimeout := Value; + if Connected then + Check(Engine, Engine.SetCommandTimeout(FHandle,FCommandTimeout)); + end; +end; + +procedure TPSQLDatabase.SetHost(const Value : string); +begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + if IsValidIP(Value) then + begin + FParams.Values['hostaddr'] := Value; + FParams.Values['host'] := ''; + end + else + begin + FParams.Values['hostaddr'] := ''; + FParams.Values['host'] := Value; + end; +end; + +procedure TPSQLDatabase.SetUseSSL(Reader: TReader); +begin + Reader.ReadBoolean(); //just ignore old property +end; + +procedure TPSQLDatabase.SetUserName(const Value : string); +begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FParams.Values['user'] := Value; +end; + +procedure TPSQLDatabase.SetUserPassword(const Value : string); +begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + + FParams.Values['password'] := Value; +end; + +procedure TPSQLDatabase.SetHandle(Value: DAChDBIDb); +begin + if Connected then Close; + if Value <> nil then + begin + FHandle := Value; + SetDatabaseFlags; + FAcquiredHandle := TRUE; + end; +end; + +procedure TPSQLDatabase.SetKeepConnection(Value: Boolean); +begin + if FKeepConnection <> Value then + FKeepConnection := Value; +end; + +procedure TPSQLDatabase.SetParams(Value: TStrings); +begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FParams.Assign(Value); +end; + +procedure TPSQLDatabase.SetDummyStr(Value: string); +begin +//dummy method for published +end; + +procedure TPSQLDatabase.SetDummyBool(Value: boolean); +begin +//dummy method for published +end; + +procedure TPSQLDatabase.SetDummyInt(Value: cardinal); +begin +//dummy method for published +end; + +procedure TPSQLDatabase.SetExclusive(Value: Boolean); +begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FExclusive := Value; +end; + +procedure TPSQLDatabase.SetReadOnly(Value: Boolean); +begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FReadOnly := Value; +end; + +function TPSQLDatabase.Engine : TPSQLEngine; +begin + Result := FEngine; +end; + +procedure TPSQLDatabase.GetStoredProcNames(Pattern: string; List: TStrings); +begin + List.BeginUpdate; + try + if Handle = nil then Connected := True; + List.Clear; + Check(Engine, Engine.OpenStoredProcList(Handle, Pattern, List)); + finally + List.EndUpdate; + end; +end; + +procedure TPSQLDatabase.GetTableNames(Pattern: string; SystemTables: Boolean; List: TStrings); +begin + List.BeginUpdate; + try + if Handle = nil then Connected := True; + List.Clear; + Check(Engine, Engine.OpenTableList(Handle, Pattern, SystemTables, List)); + finally + List.EndUpdate; + end; +end; + +procedure TPSQLDatabase.GetSchemaNames(Pattern: string; SystemSchemas: Boolean; List: TStrings); +begin + if not Assigned(List) then Exit; + List.BeginUpdate; + try + if Handle = nil then Connected := True; + List.Clear; + Check(Engine, Engine.OpenSchemaList(Handle, Pattern, SystemSchemas, List)); + finally + List.EndUpdate; + end; +end; + +function TPSQLDatabase.GetUsrName: string; +begin + Result := FParams.Values['user']; +end; + +procedure TPSQLDatabase.GetUserNames(Pattern: string; List: TStrings); +begin + List.BeginUpdate; + try + if Handle = nil then Connected := True; + List.Clear; + Check(Engine, Engine.OpenUserList(Handle,Pattern, List)); + finally + List.EndUpdate; + end; +end; + +function TPSQLDatabase.GetUserPassword: string; +begin + Result := FParams.Values['password']; +end; + +procedure TPSQLDatabase.GetCharsets(List: TStrings); +begin + List.BeginUpdate; + try + Check(Engine,Engine.GetCharacterSets(Handle,List)); + finally + List.EndUpdate; + end; +end; + +procedure TPSQLDatabase.SetCharSet(CharSet: string); +begin + if FCharSet <> CharSet then + begin + FCharSet := CharSet; + if Connected then + Engine.SetCharacterSet(Handle,CharSet) + end; +end; + +procedure TPSQLDatabase.GetDatabases(Pattern: string; List : TStrings); +var + OldConn : Boolean; + OldDbName : string; +begin + OldConn := Connected; + OldDbName := ''; + if not Connected then + begin + OldDbName := DatabaseName; + DatabaseName := 'template1'; + end; + if Handle = nil then Connected := True; + if Pattern <> '' then + Check(Engine, Engine.GetDatabases(Handle, Pattern, List)) else + Check(Engine, Engine.GetDatabases(Handle, '', List)); + Connected := OldConn; + if not Connected then + DatabaseName := OldDbName; +end; + +function TPSQLDatabase.GetNotifyItem(Index: Integer): TObject; +begin + Result := FNotifyList[Index]; +end; + +function TPSQLDatabase.GetNotifyCount: Integer; +begin + Result := FNotifyList.Count; +end; + +procedure TPSQLDatabase.AddNotify(AItem: TObject); +begin + if FNotifyList.IndexOf(AItem) >= 0 then Exit; + FNotifyList.Add(AItem); +end; + +procedure TPSQLDatabase.RemoveNotify(AItem: TObject); +var + I: Integer; +begin + I := FNotifyList.IndexOf(AItem); + if I >= 0 then + begin + try + TPSQLNotify(FNotifyList[I]).CloseNotify; + finally + FNotifyList.Delete(I); + end; + end; +end; + +procedure TPSQLDatabase.CloseNotify; +var + I: Integer; +begin + for I := 0 to FNotifyList.Count-1 do + TPSQLNotify(FNotifyList[I]).CloseNotify; +end; + +function TPSQLDatabase.GetBackendPID:Integer; +begin + Result := InvalidOID; + if Connected then + Engine.GetBackendPID(Handle, Result); +end; + + +////////////////////////////////////////////////////////// +//Constructor : TPSQLBDECallBack.Create +//Description : TPSQLBDECallBack +////////////////////////////////////////////////////////// +//Input : Engine: TPSQLEngine +// AOwner: TObject +// Handle: hDBICur +// CBType: CBType +// CBBuf: Pointer +// CBBufSize: Integer +// CallbackEvent: TPSQLBDECallbackEvent +// Chain: Boolean +////////////////////////////////////////////////////////// +constructor TPSQLBDECallBack.Create(Engine : TPSQLEngine; AOwner: TObject; Handle: hDBICur; CBType: CBType; + CBBuf: Pointer; CBBufSize: Integer; CallbackEvent: TPSQLBDECallbackEvent; + Chain: Boolean); +begin + FEngine := Engine; + FOwner := AOwner; + FHandle := Handle; + FCBType := CBType; + FCallbackEvent := CallbackEvent; +end; + +////////////////////////////////////////////////////////// +//Destructor : TPSQLBDECallBack.Destroy +//Description : Destroy TPSQLBDECallback +////////////////////////////////////////////////////////// +destructor TPSQLBDECallBack.Destroy; +begin + Inherited Destroy; +end; + +////////////////////////////////////////////////////////// +//function : TPSQLBDECallBack.Invoke +//Description : Invoke method TPSQLBDECallback +////////////////////////////////////////////////////////// +//Input : CallType: CBType +// CBInfo: Pointer +//Output : Result: CBRType +////////////////////////////////////////////////////////// +function TPSQLBDECallBack.Invoke(CallType: CBType; CBInfo: Pointer): CBRType; +begin + if (CallType = FCBType) then + Result := FCallbackEvent(CBInfo) + else + Result := cbrUSEDEF; + if Assigned(FOldCBFunc) then + Result := FOldCBFunc(CallType, FOldCBData, CBInfo); +end; + + +procedure TPSQLDatabase.GetTablespaces(Pattern: string; List: TStrings); +begin + if not Assigned(List) then Exit; + List.BeginUpdate; + try + if Handle = nil then Connected := True; + List.Clear; + Check(Engine, Engine.OpenTablespaceList(Handle, Pattern, List)); + finally + List.EndUpdate; + end; +end; + +procedure TPSQLDatabase.SetErrorVerbosity(const Value: TErrorVerbosity); +begin + if FErrorVerbosity <> Value then + begin + FErrorVerbosity := Value; + if Connected then + Engine.SetErrorVerbosity(Handle, Value) + end; +end; + +procedure TPSQLDatabase.SetSSLMode(const Value: TSSLMode); +begin + if FSSLMode <> Value then + begin + if FCheckIfActiveOnParamChange then + CheckInactive; //SSH tunneling + FSSLMode := Value; + FParams.Values['sslmode'] := SSLConsts[FSSLMode]; + end; +end; + +procedure TPSQLDatabase.Reset; +begin + CheckActive; + Check(Engine, Engine.Reset(FHandle)); +end; + +procedure TPSQLDatabase.CancelBackend(PID: Integer); +begin + CheckActive; + Check(Engine,Engine.CancelBackend(Handle,PID)); +end; + +procedure TPSQLDatabase.RegisterDirectQuery(aDirectQuery: TObject); +begin + FDirectQueryList.Add(aDirectQuery); +end; + +procedure TPSQLDatabase.UnregisterDirectQuery(aDirectQuery: TObject); +var + i : integer; +begin + i := FDirectQueryList.IndexOf(aDirectQuery); + if i <> -1 then + FDirectQueryList.Delete(i); +end; + +function TPSQLDatabase.SelectString(aSQL: string; var IsOk: boolean; + aFieldNumber: integer): string; +begin + DoConnect; + Check(Engine, Engine.SelectStringDirect(FHandle, PChar(aSQL), IsOk, Result, aFieldNumber)); +end; + +function TPSQLDatabase.SelectString(aSQL: string; var IsOk: boolean; + aFieldName: string): string; +begin + DoConnect; + Check(Engine, Engine.SelectStringDirect(FHandle, PChar(aSQL), IsOk, Result, aFieldName)); +end; + +procedure TPSQLDatabase.SelectStrings(aSQL: string; aList: TStrings; aFieldName: string); +begin + DoConnect; + Check(Engine, Engine.SelectStringsDirect(FHandle, PChar(aSQL), aList, aFieldName)); +end; + +procedure TPSQLDatabase.SelectStrings(aSQL: string; aList: TStrings; aFieldNumber: integer = 0); +begin + DoConnect; + Check(Engine, Engine.SelectStringsDirect(FHandle, PChar(aSQL), aList, aFieldNumber)); +end; + +function TPSQLDatabase.SelectStringDef(aSQL, aDefaultValue: string; + aFieldNumber: integer): string; +var + IsOk : boolean; +begin + Result := SelectString(aSQL, IsOk, aFieldNumber); + if not IsOk then + Result := aDefaultValue; +end; + +function TPSQLDatabase.SelectStringDef(aSQL, aDefaultValue, + aFieldName: string): string; +var + IsOk : boolean; +begin + Result := SelectString(aSQL, IsOk, aFieldName); + if not IsOk then + Result := aDefaultValue; +end; + +procedure TPSQLDatabase.FillAddonInfo; +begin + if not Connected or (FDatabaseID > 0) then Exit; + Engine.GetDBProps(FHandle, GetDatabaseName(), FOwner, FTablespace, + FIsTemplate,FDatabaseId, FComment); +end; + +function TPSQLDatabase.GetDatabaseComment: string; +begin + FillAddonInfo; + Result := FComment; +end; + +function TPSQLDatabase.GetDatabaseID: cardinal; +begin + FillAddonInfo; + Result := FDatabaseID; +end; + +function TPSQLDatabase.GetDatabaseName: string; +begin + Result := FParams.Values['dbname']; +end; + +function TPSQLDatabase.GetIsSSLUsed: Boolean; +begin + if Assigned(FHandle) then + Result := TNativeConnect(FHandle).IsSSLUsed + else + Result := False; +end; + +function TPSQLDatabase.GetIsTemplate: boolean; +begin + FillAddonInfo; + Result := FIsTemplate; +end; + +function TPSQLDatabase.GetIsUnicodeUsed: Boolean; +begin + if Assigned(FHandle) then + Result := TNativeConnect(FHandle).IsUnicodeUsed + else + Result := False; +end; + +function TPSQLDatabase.GetLibraryVersionAsInt: integer; +begin + if IsLibraryLoaded() then + Result := PQLibVersion() + else + Result := InvalidOID; +end; + +function TPSQLDatabase.GetDbOwner: string; +begin + FillAddonInfo; + Result := FOwner; +end; + +function TPSQLDatabase.GetGUCParamValue(const Name: string): string; +begin + if Assigned(FHandle) then + Result := TNativeConnect(FHandle).GUC.Values[Name] + else + Result := ''; +end; + +function TPSQLDatabase.GetHost: string; +begin + Result := FParams.Values['hostaddr']; + if Result = '' then + Result := FParams.Values['host']; +end; + +function TPSQLDatabase.GetTablespace: string; +begin + FillAddonInfo; + Result := FTablespace; +end; + +{ TPSQLDataSet } +procedure TPSQLDataSet.ReadByteaOpt(Reader: TReader); //deal with old missing properties +begin + if Reader.ReadBoolean then Include(FOptions, dsoByteaAsEscString); +end; + +procedure TPSQLDataSet.ReadOIDOpt(Reader: TReader); //deal with old missing properties +begin + if Reader.ReadBoolean then Include(FOptions, dsoOIDAsInt); +end; + +procedure TPSQLDataSet.DefineProperties(Filer: TFiler); +begin + inherited; + Filer.DefineProperty('ByteaAsEscString', ReadByteaOpt, nil, False); //missing + Filer.DefineProperty('OIDAsInt', ReadOIDOpt, nil, False); //missing +end; + +constructor TPSQLDataSet.Create(AOwner : TComponent); +var I: integer; +begin + Inherited Create(AOwner); + FCacheBlobs := False; + FAutoRefresh := FALSE; + FAllowSequenced := False; //Added by Nicolas Ring + +{$IFNDEF DELPHI_25} + FOptions := [dsoUseGUIDField]; +{$ENDIF} + + {$IFDEF MOBILE} + SetLength(FSetToRecBookm, SizeOf(TBookMark)); + {$ENDIF} + + if (csDesigning in ComponentState) and Assigned(AOwner) and (DBList.Count > 0) then + begin + for I := DBList.Count - 1 downto 0 do + if TCustomConnection(DBList[I]).Owner = AOwner then + begin + Database := TPSQLDatabase(DBList[I]); + Break; + end; + if not Assigned(Database) then + Database := TPSQLDatabase(DBList[DBList.Count - 1]); + end; +end; + +destructor TPSQLDataSet.Destroy; +begin + Inherited Destroy; + if FBlockReadBuf <> nil then + begin + FreeMem(FBlockReadBuf); + FBlockReadBuf := nil; + end; + SetUpdateObject(nil); +end; + +////////////////////////////////////////////////////////// +//procedure : TPSQLDataSet.OpenCursor +//Description : Open cursor +////////////////////////////////////////////////////////// +//Input : InfoQuery: Boolean +////////////////////////////////////////////////////////// +procedure TPSQLDataSet.OpenCursor(InfoQuery: Boolean); +var + I: Integer; +begin + if not Assigned(Database) then + DatabaseError('Property Database not set!', Self); + if dsoFetchOnDemand in Options then + for I := Database.DataSetCount - 1 downto 0 do + if Database.DataSets[I].Active then + DatabaseError('Cannot swith to fetch-on-demand mode since database object is used by another dataset', Self); + if not Assigned(FHandle) then + FHandle := CreateHandle(); + if not Assigned(FHandle) then + raise ENoResultSet.Create(SHandleError); + SetDBFlag(dbfOpened, TRUE); + Inherited OpenCursor(InfoQuery); + SetUpdateMode(FUpdateMode); + {$IFNDEF FPC} + SetupAutoRefresh; + {$ENDIF} +end; + +////////////////////////////////////////////////////////// +//procedure : TPSQLDataSet.CloseCursor +//Description : Close cursor +////////////////////////////////////////////////////////// +procedure TPSQLDataSet.CloseCursor; +begin + Inherited CloseCursor; + if FHandle <> nil then + begin + DestroyHandle; + FHandle := nil; + end; + FParentDataSet := nil; + SetDBFlag(dbfOpened, FALSE); +end; + +{$IFDEF DELPHI_12} +{$WARNINGS OFF} +procedure TPSQLDataset.CreateFields; +var F: TField; + I: integer; +begin + inherited CreateFields; + if FieldDefs.Count > Fields.Count then + for I := 0 to FieldDefs.Count - 1 do + if (FieldDefs[I].DataType = ftUnknown) and + not ((faHiddenCol in FieldDefs[I].Attributes) and not FieldDefs.HiddenFields) then + begin + case (FieldDefs[I] as TPSQLFieldDef).NativeDataType of + FIELD_TYPE_POINT: F := TPSQLPointField.Create(Self); + FIELD_TYPE_CIRCLE: F := TPSQLCircleField.Create(Self); + FIELD_TYPE_BOX: F := TPSQLBoxField.Create(Self); + FIELD_TYPE_LSEG: F := TPSQLLSegField.Create(Self); + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: F := TPSQLRangeField.Create(Self); + else + Continue; + end; + try + F.FieldName := FieldDefs[I].Name; + F.Required := faRequired in FieldDefs[I].Attributes; + F.ReadOnly := faReadonly in FieldDefs[I].Attributes; + F.DataSet := FieldDefs.DataSet; + F.Index := I; + except + F.Free; + raise; + end; + end; +end; +{$WARNINGS ON} +{$ENDIF DELPHI_12} + +////////////////////////////////////////////////////////// +//function : TPSQLDataSet.CreateHandle +//Description : Virtual method Create Handle will be overwritten +// in TPSQLQuery and TPSQLTable +////////////////////////////////////////////////////////// +//Output : Result: HDBICur +////////////////////////////////////////////////////////// +function TPSQLDataSet.CreateHandle: HDBICur; +begin + Result := nil; +end; + +procedure TPSQLDataSet.DestroyHandle; +begin + Engine.CloseCursor(FHandle); +end; + +procedure TPSQLDataSet.InternalInitFieldDefs; +var + I, FieldID: Integer; + FieldDescs: TFLDDescList; + ValCheckDesc: VCHKDesc; + RequiredFields: TBits; + CursorProps: CurProps; + FldDescCount, + MaxFieldID, + HiddenFieldCount: Integer; +begin + Engine.GetCursorProps(FHandle, CursorProps); + FldDescCount := CursorProps.iFields; + HiddenFieldCount := 0; + if FieldDefs.HiddenFields then + begin + if SetBoolProp(Engine, Handle, curGETHIDDENCOLUMNS, TRUE) then + begin + Engine.GetCursorProps(FHandle, CursorProps); + HiddenFieldCount := CursorProps.iFields - FldDescCount; + FldDescCount := CursorProps.iFields; + end; + end; + RequiredFields := TBits.Create; + try + MaxFieldID := GetIntProp(Engine, Handle, curMAXFIELDID); + if MaxFieldID > 0 then + RequiredFields.Size := MaxFieldID + 1 else + RequiredFields.Size := FldDescCount + 1; + for I := 1 to CursorProps.iValChecks do + begin + Engine.GetVChkDesc(FHandle, I, ValCheckDesc); + if ValCheckDesc.bRequired and not ValCheckDesc.bHasDefVal then + RequiredFields[ValCheckDesc.iFldNum] := True; + end; + SetLength(FieldDescs, FldDescCount); + Engine.GetFieldDescs(FHandle, FieldDescs); + FieldID := {$IFNDEF FPC}FieldNoOfs{$ELSE}1{$ENDIF}; + I := FieldID - 1; + FieldDefs.Clear; + while I < FldDescCount do + begin + FieldID := FieldDescs[I].iFldNum; + AddFieldDesc(FieldDescs, I, FieldID, RequiredFields, FieldDefs); + end; + if FieldDefs.HiddenFields then + begin + SetBoolProp(Engine, Handle, curGETHIDDENCOLUMNS, False); + if HiddenFieldCount > 0 then + for I := FldDescCount - HiddenFieldCount to FldDescCount - 1 do + FieldDefs[I].Attributes := FieldDefs[I].Attributes + [faHiddenCol]; + end; + finally + RequiredFields.Free; + end; +end; + +{$IFNDEF FPC} +procedure TPSQLDataSet.GetObjectTypeNames(Fields: TFields); +var + Len: integer; + I: Integer; + TypeDesc: ObjTypeDesc; + ObjectField: TObjectField; +begin + for I := 0 to Fields.Count-1 do + if Fields[I] is TObjectField then + begin + ObjectField := TObjectField(Fields[I]); + TypeDesc.iFldNum := ObjectField.FieldNo; + if (Engine.GetEngProp(hDBIObj(Handle), curFIELDTYPENAME, @TypeDesc, + SizeOf(TypeDesc), Len) = DBIERR_NONE) and (Len > 0) then + ObjectField.ObjectType := string(TypeDesc.szTypeName); + with ObjectField do + if DataType in [ftADT, ftArray] then + begin + if (DataType = ftArray) and SparseArrays and + (Fields[0].DataType = ftADT) then + GetObjectTypeNames(TObjectField(Fields[0]).Fields) else + GetObjectTypeNames(Fields); + end; + end +end; +{$ENDIF} + +procedure TPSQLDataSet.InternalOpen; +var + CursorProps: CurProps; +begin + Engine.GetCursorProps(FHandle, CursorProps); + FRecordSize := CursorProps.iRecBufSize; + BookmarkSize := CursorProps.iBookmarkSize; + FCanModify := (CursorProps.eOpenMode = dbiReadWrite) and not CursorProps.bTempTable; + FRecNoStatus := TRecNoStatus(CursorProps.ISeqNums); + FieldDefs.Updated := FALSE; + FieldDefs.Update; + GetIndexInfo; +{$WARNINGS OFF} + if (dsoForceCreateFields in FOptions) or DefaultFields then + CreateFields; +{$WARNINGS ON} + BindFields(TRUE); + {$IFNDEF FPC} + if ObjectView then GetObjectTypeNames(Fields); + {$ENDIF} + if (dsoPopulateFieldsOrigin in FOptions) then PopulateFieldsOrigin(); + InitBufferPointers(FALSE); + AllocKeyBuffers; + Engine.SetToBegin(FHandle); + PrepareCursor; + if Filter <> '' then FExprFilter := CreateExprFilter(Filter, FilterOptions, 0); + if Assigned(OnFilterRecord) then FFuncFilter := CreateFuncFilter(@TPSQLDataSet.RecordFilter, 1); + if Filtered then ActivateFilters; +end; + +procedure TPSQLDataSet.InternalClose; +begin + FFuncFilter := nil; + FExprFilter := nil; + FreeKeyBuffers; + BindFields(FALSE); + {$IFNDEF DELPHI_20}if DefaultFields then{$ENDIF} DestroyFields; + FIndexFieldCount := 0; + FKeySize := 0; + FExpIndex := FALSE; + FCaseInsIndex := FALSE; + FCanModify := FALSE; +end; + +procedure TPSQLDataSet.PrepareCursor; +begin +end; + +function TPSQLDataSet.IsCursorOpen: Boolean; +begin + Result := Handle <> nil; +end; + +procedure TPSQLDataSet.InternalHandleException; +var + O: TObject; +begin + if not Assigned(FDatabase) then Exit; + O := ExceptObject; + if not Assigned(O) then Exit; + if (O is Exception) and not (O is EAbort) and Assigned(FDatabase.FOnException) then + FDatabase.FOnException(Self, Exception(O)) +end; + +//////////////////////////////////////////////////////////// +// Record functions // +//////////////////////////////////////////////////////////// +procedure TPSQLDataSet.InitBufferPointers(GetProps: Boolean); +var + CursorProps: CurProps; +begin + if GetProps then + begin + Check(Engine, Engine.GetCursorProps(FHandle, CursorProps)); + BookmarkSize := CursorProps.iBookmarkSize; + FRecordSize := CursorProps.iRecBufSize; + end; + FBlobCacheOfs := FRecordSize + CalcFieldsSize; + FRecInfoOfs := FBlobCacheOfs + BlobFieldCount * SizeOf(Pointer); + FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo); + FRecBufSize := FBookmarkOfs + BookmarkSize; +end; + +{$IFDEF NEXTGEN} +function TPSQLDataSet.AllocRecBuf: TRecBuf; +begin + Result := NativeInt(AllocMem(FRecBufSize)); +end; + +procedure TPSQLDataSet.FreeRecBuf(var Buffer: TRecBuf); +begin + TArray(Buffer) := nil; +end; + +{$ELSE} + +function TPSQLDataSet.AllocRecordBuffer: TRecordBuffer; +begin + Result := AllocMem(FRecBufSize); +end; + +procedure TPSQLDataSet.FreeRecordBuffer(var Buffer : TRecordBuffer); +begin + Engine.CheckBuffer(FHandle, Buffer); //pasha_golub 10.08.06 + ClearBlobCache(Buffer); + FreeMem(Buffer); + Buffer := nil; +end; +{$ENDIF NEXTGEN} + +procedure TPSQLDataSet.InternalInitRecord(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); +begin + Engine.InitRecord(FHandle, Pointer(Buffer)); +end; + +procedure TPSQLDataSet.ClearBlobCache(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); +var + I: Integer; + addr: NativeUInt; +begin + if FCacheBlobs then + begin + addr := NativeUInt(Buffer) + NativeUInt(FBlobCacheOfs); + for I := 0 to Pred(BlobFieldCount) do + TBlobDataArray(addr)[ I ] := {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}; + end; +end; + +procedure TPSQLDataSet.ClearCalcFields(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); +begin + {$IFDEF DELPHI_12} + FillChar(PByte(Buffer)[FRecordSize], CalcFieldsSize, 0) + {$ELSE} + FillChar(Buffer[FRecordSize], CalcFieldsSize, 0); + {$ENDIF} +end; + +procedure TPSQLDataSet.InitRecord(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); +begin + {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED OFF}{$ENDIF} + Inherited InitRecord(Buffer); + {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED ON}{$ENDIF} + ClearBlobCache(Buffer); + with PRecInfo(Buffer + FRecInfoOfs)^ do + begin + UpdateStatus := TUpdateStatus(usInserted); + BookMarkFlag := bfInserted; + RecordNumber := -1; + end; +end; + +function TPSQLDataSet.GetRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; GetMode: TGetMode; DoCheck: Boolean): TGetResult; +var + Status: DBIResult; +begin + case GetMode of + gmCurrent: Status := Engine.GetRecord(FHandle, dbiNoLock, {$IFNDEF NEXTGEN}Buffer{$ELSE}Pointer(Buffer){$ENDIF}, @FRecProps); + gmNext: Status := Engine.GetNextRecord(FHandle, dbiNoLock, {$IFNDEF NEXTGEN}Buffer{$ELSE}Pointer(Buffer){$ENDIF}, @FRecProps); + gmPrior: Status := Engine.GetPriorRecord(FHandle, dbiNoLock, {$IFNDEF NEXTGEN}Buffer{$ELSE}Pointer(Buffer){$ENDIF}, @FRecProps); + else + Status := DBIERR_NONE; + end; + case Status of + DBIERR_NONE: + begin + with PRecInfo({$IFDEF NEXTGEN}DACPointerInt{$ENDIF}(Buffer) + FRecInfoOfs)^ do + begin + UpdateStatus := TUpdateStatus(FRecProps.iRecStatus); + BookmarkFlag := bfCurrent; + case FRecNoStatus of + rnParadox: RecordNumber := FRecProps.iSeqNum; + rnDBase: RecordNumber := FRecProps.iPhyRecNum; + else + RecordNumber := -1; + end; + end; + ClearBlobCache(Buffer); + {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED OFF}{$ENDIF} + GetCalcFields(Buffer); + {$IFDEF DELPHI_18}{$WARN SYMBOL_DEPRECATED ON}{$ENDIF} + Check(Engine, Engine.GetBookmark(FHandle, {$IFNDEF NEXTGEN}Buffer{$ELSE}PByte(Buffer){$ENDIF} + FBookmarkOfs)); + Result := grOK; + end; + DBIERR_BOF: Result := grBOF; + DBIERR_EOF: Result := grEOF; + else + Result := grError; + if DoCheck then Check(Engine, Status); + end; +end; + +function TPSQLDataSet.GetCurrentRecord(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}): Boolean; +begin + if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then + begin + UpdateCursorPos; + Result := (Engine.GetRecord(FHandle, dbiNoLock, Pointer(Buffer), nil) = DBIERR_NONE); + end else + Result := FALSE; +end; + +function TPSQLDataSet.GetOldRecord: PAnsiDACChar; +begin + UpdateCursorPos(); + + if SetBoolProp(Engine, Handle, curDELAYUPDGETOLDRECORD, TRUE) then + try + AllocCachedUpdateBuffers(True); + Check(Engine, Engine.GetRecord(FHandle, dbiNoLock, FUpdateCBBuf.pOldRecBuf, nil)); + Result := PAnsiDACChar(FUpdateCBBuf.pOldRecBuf); + AllocCachedUpdateBuffers(False); + finally + SetBoolProp(Engine, Handle, curDELAYUPDGETOLDRECORD, FALSE); + end else + Result := nil; +end; + +procedure TPSQLDataSet.FetchAll; +begin + if not EOF then + begin + CheckBrowseMode; + Check(Engine, Engine.SetToEnd(Handle)); + Check(Engine, Engine.GetPriorRecord(FHandle, dbiNoLock, nil, nil)); + CursorPosChanged; + end; +end; + +procedure TPSQLDataSet.FlushBuffers; +begin + CheckBrowseMode; +end; + +function TPSQLDataSet.GetRecordCount: Integer; +begin + CheckActive; + if Engine.GetRecordCount(FHandle, Result) <> DBIERR_NONE then + Result := -1; +end; + +function TPSQLDataSet.GetRecNo: Integer; +var + BufPtr: TRecordBuffer; +begin + CheckActive; + if (State = dsCalcFields) then + BufPtr := TRecordBuffer(CalcBuffer) + else + BufPtr := TRecordBuffer(ActiveBuffer); + Result := PRecInfo(BufPtr + FRecInfoOfs).RecordNumber; +end; + +procedure TPSQLDataSet.SetRecNo(Value : Integer); +begin + CheckBrowseMode; + if (FRecNoStatus = rnParadox) and (Value <> RecNo) then + begin + DoBeforeScroll; + if Engine.SetToSeqNo(Handle, Value) = DBIERR_NONE then + begin + Resync([rmCenter]); + DoAfterScroll; + end; + end; +end; + +function TPSQLDataSet.GetRecordSize: integer; +begin + Result := FRecordSize; +end; + +function TPSQLDataSet.GetActiveRecBuf(var RecBuf: TRecordBuffer): Boolean; +begin + case State of + dsBlockRead: + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(FBlockReadBuf + (FBlockBufOfs * FRecordSize)); + {$ELSE} + RecBuf := FBlockReadBuf + (FBlockBufOfs * FRecordSize); + {$ENDIF} + + dsBrowse: if IsEmpty then + Pointer(RecBuf) := nil + else + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(ActiveBuffer); + {$ELSE} + RecBuf := ActiveBuffer; + {$ENDIF} + + dsEdit, dsInsert: + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(ActiveBuffer); + {$ELSE} + RecBuf := ActiveBuffer; + {$ENDIF} + + dsSetKey: + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(PByte(FKeyBuffer) + SizeOf(TKeyBuffer)); + {$ELSE} + RecBuf := PAnsiChar(FKeyBuffer) + SizeOf(TKeyBuffer); + {$ENDIF} + + dsCalcFields: + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(CalcBuffer); + {$ELSE} + RecBuf := CalcBuffer; + {$ENDIF} + + dsFilter: + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(FFilterBuffer); + {$ELSE} + RecBuf := FFilterBuffer; + {$ENDIF} + + dsNewValue: if FInUpdateCallback then + Pointer(RecBuf) := FUpdateCBBuf.pNewRecBuf + else + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(ActiveBuffer); + {$ELSE} + RecBuf := ActiveBuffer; + {$ENDIF} + + dsOldValue: if FInUpdateCallback then + Pointer(RecBuf) := FUpdateCBBuf.pOldRecBuf + else + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(GetOldRecord); + {$ELSE} + RecBuf := GetOldRecord; + {$ENDIF} + + else + Pointer(RecBuf) := nil; + end; + Result := Pointer(RecBuf) <> nil; +end; + +procedure TPSQLDataSet.AddFieldDesc(FieldDescs: TFLDDescList; var DescNo: Integer; + var FieldID: Integer; RequiredFields: TBits; FieldDefs: TFieldDefs); +var + FType: TFieldType; +{$IFDEF DELPHI_12} + ANativeType: cardinal; +{$ENDIF} + FSize: integer; + FRequired: Boolean; + FPrecision, I: Integer; + FieldName, FName: string; + FieldDesc: FLDDesc; +begin + FieldDesc := FieldDescs[DescNo]; + Inc(DescNo); + with FieldDesc do + begin + FieldName := szName; + {$IFDEF DELPHI_12}ANativeType := iNativeType;{$ENDIF} + I := 0; + FName := FieldName; + while FieldDefs.IndexOf(string(FName)) >= 0 do + begin + Inc(I); + FName := Format('%s_%d', [string(FieldName), I]); + end; + if iFldType < MAXLOGFLDTYPES then + FType := DataTypeMap[iFldType] + else + FType := ftUnknown; + FSize := 0; + FPrecision := 0; + if RequiredFields.Size > FieldID then + FRequired := RequiredFields[FieldID] else + FRequired := False; + case iFldType of + fldZSTRING, fldBYTES, fldVARBYTES, fldADT, fldArray, fldRef: + begin + FSize := iUnits1; + end; + fldINT16, fldUINT16: + if iLen <> 2 then FType := ftUnknown; + fldINT32: + if iSubType = fldstAUTOINC then + begin + FType := ftAutoInc; + FRequired := False; + end; + fldFLOAT: + if iSubType = fldstMONEY then FType := ftCurrency; + fldBCD + {$IFDEF DELPHI_12} + , fldFmtBCD + {$ENDIF}: + begin + FSize := Abs(iUnits2); + FPrecision := iUnits1; + end; + fldBLOB: + begin + FSize := iUnits1; + if (iSubType >= fldstMEMO) and (iSubType <= fldstBFILE) then + FType := BlobTypeMap[iSubType]; + end; + fldUUID: + begin + FSize := PSQLTypes.UUIDLEN; + FType := ftGuid; + end; + end; + + //pg: Unicode playing + {$IFDEF DELPHI_12} + if TNativeConnect(FDatabase.Handle).IsUnicodeUsed then + case FType of + ftString: FType := ftWideString; + ftMemo: FType := ftWideMemo; + ftFixedChar: FType := ftFixedWideChar; + end; + {$ENDIF} + + + with FieldDefs.AddFieldDef {$IFDEF DELPHI_12}as TPSQLFieldDef{$ENDIF} do + begin + {$IFNDEF FPC}FieldNo := FieldID;{$ENDIF} + Inc(FieldID); + Name := FName; + {$IFDEF DELPHI_12}NativeDataType := ANativeType;{$ENDIF} + DataType := FType; + Size := FSize; + Precision := FPrecision; + if FRequired then + Attributes := [faRequired]; + if efldrRights = fldrREADONLY then + Attributes := Attributes + [faReadonly]; + if iSubType = fldstFIXED then + Attributes := Attributes + [faFixed]; + InternalCalcField := bCalcField; + case FType of + ftADT: + begin + if iSubType = fldstADTNestedTable then + Attributes := Attributes + [faUnNamed]; + for I := 0 to iUnits1 - 1 do + AddFieldDesc(FieldDescs, DescNo, FieldID, RequiredFields, {$IFNDEF FPC}ChildDefs{$ELSE}nil{$ENDIF}); + end; + ftArray: + begin + I := FieldID; + FieldDescs[DescNo].szName := FieldDesc.szName + '[0]'; + AddFieldDesc(FieldDescs, DescNo, I, RequiredFields, {$IFNDEF FPC}ChildDefs{$ELSE}nil{$ENDIF}); + Inc(FieldID, iUnits2); + end; + end; + end; + end; +end; + +{$IFNDEF FPC} +function TPSQLDataSet.GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer; +var + RecBuf: TRecordBuffer; + Status: DBIResult; + DoCheck: Boolean; +begin + Result := 0; + DoCheck := (BlockReadSize = 0); + if (BlockReadSize > 0) then + {$IFDEF DELPHI_12} + RecBuf := TRecordBuffer(FBlockReadBuf + (FBlockBufOfs * FRecordSize)) + {$ELSE} + RecBuf := FBlockReadBuf + (FBlockBufOfs * FRecordSize) + {$ENDIF} + else + if not GetActiveRecBuf(RecBuf) then Exit; + Status := Engine.OpenBlob(FHandle, Pointer(RecBuf), FieldNo, dbiReadOnly); + if (Status <> DBIERR_NONE) then + Exit; + try + Status := Engine.GetBlobSize(FHandle, Pointer(RecBuf), FieldNo, Result); + if (Status <> DBIERR_NONE) or (Result = 0) then Exit; + if (High(Buffer) <= Result) then + SetLength(Buffer, Trunc(Result + Result div 4)); + Status := Engine.GetBlob(FHandle, Pointer(RecBuf), FieldNo, 0, Result, Buffer, Result); + finally + if (Status <> DBIERR_NONE) then + Result := 0; + Engine.FreeBlob(FHandle, Pointer(RecBuf), FieldNo); + if DoCheck then + Check(Engine, Status) + end; +end; +{$ENDIF} + +{$IFDEF DELPHI_12} +function TPSQLDataSet.GetFieldClass(FieldDef: TFieldDef): TFieldClass; +begin + if FieldDef.DataType = ftUnknown then + case TPSQLFieldDef(FieldDef).NativeDataType of + FIELD_TYPE_POINT: Result := TPSQLPointField; + FIELD_TYPE_CIRCLE: Result := TPSQLCircleField; + FIELD_TYPE_BOX: Result := TPSQLBoxField; + FIELD_TYPE_LSEG: Result := TPSQLLSegField; + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: Result := TPSQLRangeField; + else + Result := inherited GetFieldClass(FieldDef); + end + else + Result := inherited GetFieldClass(FieldDef); +end; +{$ENDIF} + +{$IFNDEF NEXTGEN} +function TPSQLDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; +var + IsBlank: Boolean; + RecBuf: TRecordBuffer; + Status: DBIResult; +begin +{$IFNDEF FPC} + if (BlockReadSize > 0) then + begin + Status := Engine.GetField(FHandle, FieldNo, FBlockReadBuf + + (FBlockBufOfs * FRecordSize), Buffer, IsBlank); + Result := (Status = DBIERR_NONE) and not IsBlank; + end + else +{$ENDIF} + begin + Result := GetActiveRecBuf(RecBuf); + if Result then + begin + Check(Engine, Engine.GetField(FHandle, FieldNo, RecBuf, Buffer, IsBlank)); + Result := not IsBlank; + end + end; +end; + +function TPSQLDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean; +var + RecBuf: TRecordBuffer; +begin + if (Field.FieldNo > 0) then + Result := GetFieldData(Field.FieldNo, Buffer) + else + begin + if (State = dsBlockRead) then + begin + RecBuf := TRecordBuffer(TempBuffer); + Result := TRUE; + end + else + Result := GetActiveRecBuf(RecBuf); + if Result and (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsBlockRead]) then + begin + Inc(RecBuf, FRecordSize + Field.Offset); + Result := Boolean(RecBuf[ 0 ]); + if Result and (Buffer <> nil) then + Move(RecBuf[1], Buffer^, Field.DataSize); + end; + end; +end; + +{$ENDIF} + +{$IFDEF DELPHI_12} +function TPSQLDataSet.GetFieldDefsClass: TFieldDefsClass; +begin + Result := TPSQLFieldDefs; +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +function TPSQLDataSet.GetFieldData(Field: TField; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; +var + RecBuf: TRecordBuffer;//PByte; +begin + if Field.FieldNo > 0 then + Result := GetFieldData(Field.FieldNo, Buffer) + else + begin + if State = dsBlockRead then + begin + RecBuf := {PByte}TRecordBuffer(TempBuffer); + Result := True; + end else + Result := GetActiveRecBuf(RecBuf); + if Result and (State in [dsBrowse, dsEdit, dsInsert, dsCalcFields, dsBlockRead]) then + begin + Result := Boolean(PByte(RecBuf)[FRecordSize + Field.Offset]); + if Result and (Buffer <> nil) then + Move(PByte(RecBuf)[FRecordSize + Field.Offset + 1], Buffer[0], Field.DataSize); + end; + end; +end; + +function TPSQLDataSet.GetFieldData(FieldNo: Integer; {$IFDEF DELPHI_18}var{$ENDIF} Buffer: TValueBuffer): Boolean; +var + IsBlank: Boolean; + RecBuf: TRecordBuffer;//PByte; + Status: DBIResult; +begin + if BlockReadSize > 0 then + begin + { Optimized for speed. If error, just return false } + Status := Engine.GetField(FHandle, FieldNo, PByte(FBlockReadBuf) + + (FBlockBufOfs * FRecordSize), Buffer, IsBlank); + Result := (Status = DBIERR_NONE) and not IsBlank; + end else + begin + Result := GetActiveRecBuf(RecBuf); + if Result then + begin + Check(Engine, Engine.GetField(FHandle, FieldNo, Pointer(RecBuf), Buffer, IsBlank)); + Result := not IsBlank; + end + end; +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLDataSet.SetFieldData(Field: TField; Buffer: TValueBuffer); +var + RecBuf: TRecordBuffer;//PByte; +begin + if not (State in dsWriteModes) then DatabaseError(SNotEditing, Self); + if (State = dsSetKey) and ((Field.FieldNo < 0) or (FIndexFieldCount > 0) and + not Field.IsIndexField) then DatabaseErrorFmt(SNotIndexField, [Field.DisplayName]); + GetActiveRecBuf(RecBuf); + if Field.FieldNo > 0 then + begin + if State = dsCalcFields then DatabaseError(SNotEditing, Self); + if Field.ReadOnly and not (State in [dsSetKey, dsFilter]) then + DatabaseErrorFmt(SFieldReadOnly, [Field.DisplayName]); + Field.Validate(Buffer); + if Field.FieldKind <> fkInternalCalc then + Check(Engine, Engine.PutField(FHandle, Field.FieldNo, Pointer(RecBuf), @Buffer[0])); + end + else {fkCalculated, fkLookup} + begin + Boolean(PByte(RecBuf)[FRecordSize + Field.Offset]) := NativeUInt(Buffer) > 0; //was LongBool(Buffer) + if Boolean(PByte(RecBuf)[FRecordSize + Field.Offset]) then + Move(Buffer[0], PByte(RecBuf)[FRecordSize + Field.Offset + 1], Field.DataSize); + end; + if not (State in [dsCalcFields, dsFilter, dsNewValue]) then + DataEvent(deFieldChange, TDataEventInfo(Field)); +end; +{$ENDIF} + +{$IFNDEF NEXTGEN} +procedure TPSQLDataSet.SetFieldData(Field: TField; Buffer: Pointer); +var + RecBuf: TRecordBuffer; +{$IFDEF DELPHI_17} + AValueBuffer: TValueBuffer; +{$ENDIF} +begin + with Field do + begin + if not (State in dsWriteModes) then + DatabaseError(SNotEditing, Self); + if (State = dsSetKey) and ((FieldNo < 0) or (FIndexFieldCount > 0) and + not IsIndexField) then + DatabaseErrorFmt(SNotIndexField, [DisplayName], Self); + GetActiveRecBuf(RecBuf); + if (FieldNo > 0) then + begin + if (State = dsCalcFields) then DatabaseError(SNotEditing); + if ReadOnly and not (State in [dsSetKey, dsFilter]) then + DatabaseErrorFmt(SFieldReadOnly, [DisplayName]); + {$IFDEF DELPHI_17} + AValueBuffer := TValueBuffer(Buffer); + Validate(AValueBuffer); + {$ELSE} + Validate(Buffer); + {$ENDIF} + if FieldKind <> fkInternalCalc then + Check(Engine, Engine.PutField(FHandle, FieldNo, RecBuf, Buffer)); + end + else {fkCalculated, fkLookup} + begin + Inc(RecBuf, FRecordSize + Offset); + Boolean(RecBuf[0]) := LongBool(Buffer); + if Boolean(RecBuf[ 0 ]) then + Move(Buffer^, RecBuf[ 1 ], DataSize); + end; + if not (State in [dsCalcFields, dsFilter, dsNewValue]) then + DataEvent(deFieldChange, TDataEventInfo(Field)); + end; +end; +{$ENDIF} + +function TPSQLDataSet.GetBlobData(Field : TField; Buffer : TRecordBuffer) : TBlobData; +begin + Result := TBlobDataArray(Buffer + FBlobCacheOfs)[ Field.Offset ]; +end; + +procedure TPSQLDataSet.SetBlobData(Field : TField; Buffer : TRecordBuffer; Value : TBlobData); +var + addr: NativeUInt; +begin + if (Buffer = TRecordBuffer(ActiveBuffer)) then + begin + addr := NativeUInt(Buffer) + NativeUInt(FBlobCacheOfs); + TBlobDataArray(addr)[ Field.Offset ] := Value; + end; +end; + +procedure TPSQLDataSet.CloseBlob(Field: TField); +begin + Engine.FreeBlob(Handle, Pointer(ActiveBuffer), Field.FieldNo); +end; + +{$IFNDEF FPC} +function TPSQLDataSet.GetStateFieldValue(State: TDataSetState; Field: TField): Variant; +var Param: TPSQLParam; +begin + CheckCachedUpdateMode; + if State = dsOldValue then + begin + Param := TPSQLParam.Create(nil); + try + Engine.GetFieldOldValue(Handle, Field.FieldName, Param); + Result := Param.Value; + finally + Param.Free; + end; + end + else + Result := Inherited GetStateFieldValue(State, Field); +end; + +procedure TPSQLDataSet.SetStateFieldValue(State: TDataSetState; Field: TField; Const Value: Variant); +begin + CheckCachedUpdateMode; + Inherited SetStateFieldValue(State, Field, Value); +end; + +function TPSQLDataSet.GetFieldFullName(Field : TField) : string; +begin + Result := inherited GetFieldFullName(Field); +end; +{$ENDIF} + +procedure TPSQLDataSet.InternalFirst; +begin + Check(Engine, Engine.SetToBegin(FHandle)); +end; + +procedure TPSQLDataSet.InternalLast; +begin + Check(Engine, Engine.SetToEnd(FHandle)); +end; + +procedure TPSQLDataSet.InternalEdit; +begin + FOldBuffer := {$IFNDEF NEXTGEN}AllocRecordBuffer{$ELSE}AllocRecBuf{$ENDIF}; + Move(Pointer(ActiveBuffer)^, {$IFDEF NEXTGEN}PByte{$ENDIF}(FOldBuffer)[0], FRecBufSize); + Check(Engine, Engine.GetRecord(FHandle, dbiWriteLock, Pointer(ActiveBuffer), nil)); + ClearBlobCache({$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}(ActiveBuffer)); +end; + +procedure TPSQLDataSet.InternalInsert; +begin + SetBoolProp(Engine, Handle, curMAKECRACK, TRUE); + CursorPosChanged; +end; + +procedure TPSQLDataSet.InternalPost; +begin + if Assigned(FUpdateObject) then + begin + if State = dsEdit then + FUpdateObject.Apply(ukModify) + else + if State = dsInsert then + FUpdateObject.Apply(ukInsert); + end //if assigned + else + begin + {$IFDEF DELPHI_6} + inherited; //mi:2008-02-13 Moved from begining of the method. Actually there is only CheckRequiredFields there. + // So we don't need it if dataset is being updated by UpdateObject + {$ENDIF}//pasha_golub 10.08.06 + + if State = dsEdit then + Check(Engine, Engine.ModifyRecord(FHandle, Pointer(FOldBuffer), Pointer(ActiveBuffer), TRUE,RecNo)) + else + if State = dsInsert then + Check(Engine, Engine.InsertRecord(FHandle, dbiNoLock, Pointer(ActiveBuffer))); + end; //else + if assigned(Pointer(fOldBuffer)) then + {$IFNDEF NEXTGEN} + FreeRecordBuffer(FOldBuffer); + {$ELSE} + FreeRecBuf(FOldBuffer); + {$ENDIF} +end; + +procedure TPSQLDataSet.InternalDelete; +var + Result: Word; +begin + if not Assigned(FUpdateObject) then + begin + Result := Engine.DeleteRecord(FHandle, Pointer(ActiveBuffer)); + if (Result <> DBIERR_NONE) then Check(Engine, Result); + end + else + FUpdateObject.Apply(ukDelete); +end; + +function TPSQLDataSet.IsSequenced: Boolean; +begin + Result := (FRecNoStatus = rnParadox) and (not Filtered); +end; + +function TPSQLDataSet.GetCanModify: Boolean; +begin + Result := FCanModify or ForceUpdateCallback; +end; + +procedure TPSQLDataSet.InternalRefresh; +begin + Check(Engine, Engine.ForceReread(FHandle)); +end; + +procedure TPSQLDataSet.Post; +begin + Inherited Post; + if (State = dsSetKey) then + PostKeyBuffer(TRUE); +end; + +procedure TPSQLDataSet.Cancel; +begin + Inherited Cancel; + if State = dsSetKey then + PostKeyBuffer(FALSE); +end; + +procedure TPSQLDataSet.InternalCancel; +begin + if State = dsEdit then + Engine.RelRecordLock(FHandle, FALSE); + if assigned(Pointer(fOldBuffer)) then + {$IFNDEF NEXTGEN} + FreeRecordBuffer(FOldBuffer); + {$ELSE} + FreeRecBuf(fOldBuffer); + {$ENDIF} +end; + +procedure TPSQLDataSet.InternalAddRecord(Buffer: {$IFNDEF NEXTGEN}Pointer{$ELSE}TRecBuf{$ENDIF}; Append: Boolean); +begin + if Append then + Check(Engine, Engine.AppendRecord(FHandle, Pointer(Buffer))) else + Check(Engine, Engine.InsertRecord(FHandle, dbiNoLock, Pointer(Buffer))); +end; + +{$IFDEF NEXTGEN} +procedure TPSQLDataSet.InternalGotoBookmark(Bookmark: TBookmark); +begin + Check(Engine, Engine.SetToBookmark(FHandle, Bookmark)); +end; +{$ELSE} +procedure TPSQLDataSet.InternalGotoBookmark(Bookmark : Pointer); +begin + Check(Engine, Engine.SetToBookmark(FHandle, Bookmark)); +end; +{$ENDIF} + +procedure TPSQLDataSet.InternalSetToRecord(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}); +begin +{$IFNDEF NEXTGEN} + InternalGotoBookmark(Pointer(Buffer + FBookmarkOfs)); +{$ELSE} + Move(Pointer(DACPointerInt(Buffer) + FBookmarkOfs)^, FSetToRecBookm[0], SizeOf(TBookMark)); + InternalGotoBookmark(FSetToRecBookm); +{$ENDIF} +end; + +function TPSQLDataSet.GetBookmarkFlag(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}) : TBookmarkFlag; +begin + Result := PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag; +end; + +procedure TPSQLDataSet.SetBookmarkFlag(Buffer : {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Value : TBookmarkFlag); +begin + PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag := Value; +end; + +{$IFNDEF NEXTGEN} +procedure TPSQLDataSet.GetBookmarkData(Buffer : TRecordBuffer; Data : Pointer); +begin + Move(Buffer[FBookmarkOfs], Data^, BookmarkSize); +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLDataSet.GetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); +begin + Move(PByte(Buffer)[FBookmarkOfs], Data[0], BookmarkSize); +end; +{$ENDIF DELPHI_17} + +{$IFNDEF NEXTGEN} +procedure TPSQLDataSet.SetBookmarkData(Buffer : TRecordBuffer; Data : Pointer); +begin + Move(Data^, Buffer[FBookmarkOfs], BookmarkSize); +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLDataSet.SetBookmarkData(Buffer: {$IFNDEF NEXTGEN}TRecordBuffer{$ELSE}TRecBuf{$ENDIF}; Data: TBookmark); +begin + Move(Data[0], PByte(Buffer)[FBookmarkOfs], BookmarkSize); +end; +{$ENDIF DELPHI_17} + +function TPSQLDataSet.CompareBookmarks(Bookmark1, Bookmark2 : TBookmark) : Integer; +const + RetCodes: array[Boolean, Boolean] of ShortInt = ((2,CMPLess),(CMPGtr,CMPEql)); +begin + { Check for uninitialized bookmarks } + Result := RetCodes[Bookmark1 = nil, Bookmark2 = nil]; + if (Result = 2) then + begin + if (Handle <> nil) then + Check(Engine, Engine.CompareBookmarks(Handle, Bookmark1, Bookmark2, Result)); + if (Result = CMPKeyEql) then + Result := CMPEql; + end; +end; + +function TPSQLDataSet.BookmarkValid(Bookmark: TBookmark): Boolean; +begin + Result := (Handle <> nil); + if Result then + begin + CursorPosChanged; + Result := (Engine.SetToBookmark(FHandle, Bookmark) = DBIERR_NONE) and + (Engine.GetRecord(FHandle, dbiNOLOCK, nil, nil) = DBIERR_NONE) + end; +end; + +{$IFNDEF FPC} +procedure TPSQLDataSet.SetBlockReadSize(Value: Integer); + + function CanBlockRead: Boolean; + var + i: Integer; + begin + Result := (BufferCount <= 1) and (DataSetField = nil); + if Result then + for i := 0 to FieldCount - 1 do + if (Fields[i].DataType in [ftDataSet, ftReference]) then + begin + Result := False; + break; + end; + end; + + procedure FreeBuffer; + begin + if FBlockReadBuf <> nil then + begin + FreeMem(FBlockReadBuf); + FBlockReadBuf := nil; + end; + end; + +const + DEFBLOCKSIZE = 64 * 1024; +var + Size: Integer; +begin + if Value <> BlockReadSize then + begin + if Value > 0 then + begin + if EOF or not CanBlockRead then Exit; + FreeBuffer; + UpdateCursorPos; + +//mi:2008-03-25 #0766 curMAKECRACK flag set native dataset to tsEmpty mode. But we need to set it in tsFirst mode! +// Engine.SetEngProp(HDBIObj(FHandle), curMAKECRACK, 0); + TNativeDataSet(FHandle).RecordState := tsFirst; + + if Value = MaxInt then + Size := DEFBLOCKSIZE + else + Size := Value * FRecordSize; + + FBlockReadBuf := AllocMem(Size); + FBlockBufSize := Size div FRecordSize; + FBlockBufOfs := FBlockBufSize; { Force read of data } + FBlockBufCount := FBlockBufSize; + FBlockReadCount := 0; + + inherited; + + BlockReadNext(); + end + else + begin + inherited; +// CursorPosChanged; +// Resync([]); + FreeBuffer; + end; + end; +end; + +procedure TPSQLDataSet.BlockReadNext; +var + Status: DbiResult; +begin + if FBlockBufOfs >= FBlockBufCount - 1 then + begin + if FBlockBufCount < FBlockBufSize then + Last() + else + begin + Status := Engine.ReadBlock(FHandle, FBlockBufCount, FBlockReadBuf); + + if (Status <> DBIERR_NONE) and (Status <> DBIERR_EOF) then + Check(Engine,Status); + + if (FBlockBufCount = 0) and (Status = DBIERR_EOF) then + Last(); + Inc(FBlockReadCount, FBlockBufCount); + FBlockBufOfs := 0; + end + end + else + Inc(FBlockBufOfs); + + if CalcFieldsSize > 0 then + GetCalcFields(TempBuffer); + + DataEvent(deDataSetScroll, -1); +end; +{$ENDIF} + +procedure TPSQLDataSet.GetIndexInfo; +var + IndexDesc: IDXDesc; +begin + if Engine.GetIndexDesc(FHandle, 0, IndexDesc) = DBIERR_NONE then + begin + FExpIndex := IndexDesc.bExpIdx; + FCaseInsIndex := IndexDesc.bCaseInsensitive; + if not ExpIndex then + begin + FIndexFieldCount := IndexDesc.iFldsInKey; + FIndexFieldMap := IndexDesc.aiKeyFld; + end; + FKeySize := IndexDesc.iKeyLen; + end; +end; + + +procedure TPSQLDataSet.SwitchToIndex(const IndexName, TagName : string); +var + Status: DBIResult; +begin + ResetCursorRange; + UpdateCursorPos; + Status := Engine.SwitchToIndex(FHandle, IndexName, TagName, 0, TRUE); + if (Status = DBIERR_NOCURRREC) then + Status := Engine.SwitchToIndex(FHandle, IndexName, TagName, 0, FALSE); + Check(Engine, Status); + FKeySize := 0; + FExpIndex := FALSE; + FCaseInsIndex := FALSE; + FIndexFieldCount := 0; + SetBufListSize(0); + InitBufferPointers(TRUE); + try + SetBufListSize(BufferCount + 1); + except + SetState(dsInactive); + CloseCursor; + raise; + end; + GetIndexInfo; +end; + +function TPSQLDataSet.GetIndexField(Index : Integer): TField; +var + FieldNo: Integer; +begin + if (Index < 0) or (Index >= FIndexFieldCount) then DatabaseError(SFieldIndexError, Self); + FieldNo := FIndexFieldMap[Index]; + Result := FieldByNumber(FieldNo); + if Result = nil then DatabaseErrorFmt(SIndexFieldMissing, [ FieldDefs[FieldNo - 1].Name ], Self); +end; + +procedure TPSQLDataSet.SetIndexField(Index : Integer; Value : TField); +begin + GetIndexField(Index).Assign(Value); +end; + +function TPSQLDataSet.GetIndexFieldCount: Integer; +begin + Result := FIndexFieldCount; +end; + +procedure TPSQLDataSet.AllocKeyBuffers; +var + KeyIndex: TKeyIndex; +begin + try + for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do + FKeyBuffers[KeyIndex] := InitKeyBuffer(AllocMem(SizeOf(TKeyBuffer) + FRecordSize)); + except + FreeKeyBuffers; + raise; + end; +end; + +procedure TPSQLDataSet.FreeKeyBuffers; +var + KeyIndex: TKeyIndex; +begin + for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do + DisposeMem(FKeyBuffers[ KeyIndex ], SizeOf(TKeyBuffer) + FRecordSize); +end; + +function TPSQLDataSet.InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer; +begin + FillChar(Buffer^, SizeOf(TKeyBuffer) + FRecordSize, 0); + Engine.InitRecord(FHandle, PAnsiDACChar(Buffer) + SizeOf(TKeyBuffer)); + Result := Buffer; +end; + +procedure TPSQLDataSet.CheckSetKeyMode; +begin + if (State <> dsSetKey) then DatabaseError(SNotEditing, Self); +end; + +function TPSQLDataSet.SetCursorRange: Boolean; +var + RangeStart, RangeEnd: PKeyBuffer; + StartKey, EndKey: PAnsiDACChar; + IndexBuffer: PAnsiDACChar; + UseStartKey, UseEndKey, UseKey: Boolean; +begin + Result := FALSE; + if not (BuffersEqual(FKeyBuffers[kiRangeStart], FKeyBuffers[kiCurRangeStart],SizeOf(TKeyBuffer) + FRecordSize) and + BuffersEqual(FKeyBuffers[kiRangeEnd], FKeyBuffers[kiCurRangeEnd],SizeOf(TKeyBuffer) + FRecordSize)) then + begin + IndexBuffer := AllocMem(KeySize * 2); + try + UseStartKey := TRUE; + UseEndKey := TRUE; + RangeStart := FKeyBuffers[kiRangeStart]; + if RangeStart.Modified then + begin + StartKey := PAnsiDACChar(RangeStart) + SizeOf(TKeyBuffer); + UseStartKey := Engine.ExtractKey(Handle, StartKey, IndexBuffer) = 0; + end + else + StartKey := nil; + RangeEnd := FKeyBuffers[kiRangeEnd]; + if RangeEnd.Modified then + begin + EndKey := PAnsiDACChar(RangeEnd) + SizeOf(TKeyBuffer); + UseEndKey := (Engine.ExtractKey(Handle, EndKey, IndexBuffer + KeySize) = 0); + end + else + EndKey := nil; + UseKey := UseStartKey and UseEndKey; + if UseKey then + begin + if (StartKey <> nil) then + StartKey := IndexBuffer; + if (EndKey <> nil) then + EndKey := IndexBuffer + KeySize; + end; + Check(Engine, Engine.SetRange(FHandle, UseKey, + RangeStart.FieldCount, 0, StartKey, not RangeStart.Exclusive, + RangeEnd.FieldCount, 0, EndKey, not RangeEnd.Exclusive)); + Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiCurRangeStart]^, + SizeOf(TKeyBuffer) + FRecordSize); + Move(FKeyBuffers[kiRangeEnd]^, FKeyBuffers[kiCurRangeEnd]^, + SizeOf(TKeyBuffer) + FRecordSize); + Result := TRUE; + finally + FreeMem(IndexBuffer, KeySize * 2); + end; + end; +end; + +function TPSQLDataSet.ResetCursorRange: Boolean; +begin + Result := FALSE; + if FKeyBuffers[kiCurRangeStart].Modified or + FKeyBuffers[kiCurRangeEnd].Modified then + begin + Check(Engine, Engine.ResetRange(FHandle)); + InitKeyBuffer(FKeyBuffers[kiCurRangeStart]); + InitKeyBuffer(FKeyBuffers[kiCurRangeEnd]); + Result := TRUE; + end; +end; + +procedure TPSQLDataSet.SetLinkRanges(MasterFields: TList{$IFDEF DELPHI_17}{$ENDIF}); +var + I: Integer; + SaveState: TDataSetState; +begin + SaveState := SetTempState(dsSetKey); + try + FKeyBuffer := InitKeyBuffer(FKeyBuffers[kiRangeStart]); + FKeyBuffer^.Modified := TRUE; + for I := 0 to Pred(MasterFields.Count) do + GetIndexField(I).Assign(TField(MasterFields[I])); + FKeyBuffer^.FieldCount := MasterFields.Count; + finally + RestoreState(SaveState); + end; + Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiRangeEnd]^, + SizeOf(TKeyBuffer) + FRecordSize); +end; + +function TPSQLDataSet.GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer; +begin + Result := FKeyBuffers[KeyIndex]; +end; + +procedure TPSQLDataSet.SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean); +begin + CheckBrowseMode; + FKeyBuffer := FKeyBuffers[KeyIndex]; + Move(FKeyBuffer^, FKeyBuffers[kiSave]^, SizeOf(TKeyBuffer) + FRecordSize); + if Clear then InitKeyBuffer(FKeyBuffer); + SetState(dsSetKey); + SetModified(FKeyBuffer.Modified); + DataEvent(deDataSetChange, 0); +end; + +procedure TPSQLDataSet.PopulateFieldsOrigin(); +var I: integer; +begin + for I := 0 to Fields.Count -1 do + Fields[I].Origin := Engine.GetFieldOrigin(FHandle, Fields[I].FieldNo) +end; + +procedure TPSQLDataSet.PostKeyBuffer(Commit: Boolean); +begin + DataEvent(deCheckBrowseMode, 0); + if FKeyBuffer^.FieldCount = 0 then + FKeyBuffer^.FieldCount := FIndexFieldCount; + if Commit then + FKeyBuffer.Modified := Modified + else + Move(FKeyBuffers[kiSave]^, FKeyBuffer^, SizeOf(TKeyBuffer) + FRecordSize); + SetState(dsBrowse); + DataEvent(deDataSetChange, 0); +end; + +function TPSQLDataSet.GetKeyExclusive: Boolean; +begin + CheckSetKeyMode; + Result := FKeyBuffer.Exclusive; +end; + +procedure TPSQLDataSet.SetKeyExclusive(Value: Boolean); +begin + CheckSetKeyMode; + FKeyBuffer.Exclusive := Value; +end; + +function TPSQLDataSet.GetKeyFieldCount: Integer; +begin + CheckSetKeyMode; + Result := FKeyBuffer.FieldCount; +end; + +procedure TPSQLDataSet.SetKeyFieldCount(Value: Integer); +begin + CheckSetKeyMode; + FKeyBuffer.FieldCount := Value; +end; + +procedure TPSQLDataSet.SetKeyFields(KeyIndex: TKeyIndex; + const Values: array of const); +var + I: Integer; + SaveState: TDataSetState; +begin + if ExpIndex then + DatabaseError(SCompositeIndexError, Self); + if (FIndexFieldCount = 0) then + DatabaseError(SNoFieldIndexes, Self); + SaveState := SetTempState(dsSetKey); + try + FKeyBuffer := InitKeyBuffer(FKeyBuffers[KeyIndex]); + for I := 0 to High(Values) do + GetIndexField(I).AssignValue(Values[I]); + FKeyBuffer^.FieldCount := High(Values) + 1; + FKeyBuffer^.Modified := Modified; + finally + RestoreState(SaveState); + end; +end; + +function TPSQLDataSet.GetIsIndexField(Field: TField): Boolean; +var + I: Integer; +begin + Result := FALSE; + with Field do + if (FieldNo > 0) then + for I := 0 to Pred(FIndexFieldCount) do + if (FIndexFieldMap[I] = FieldNo) then + begin + Result := TRUE; + Exit; + end; +end; + +procedure TPSQLDataSet.Notification(AComponent: TComponent; Operation: TOperation); +begin + Inherited Notification(AComponent, Operation); + if (Operation = opRemove) and (AComponent = FDatabase) then + begin + Close; + FDatabase := nil; + end; +end; + +procedure TPSQLDataSet.ActivateFilters; +begin + if FExprFilter <> nil then + begin + if Engine.ActivateFilter(FHandle, FExprFilter) <> DBIERR_NONE then + begin + Engine.DropFilter(FHandle, FExprFilter); + FExprFilter := CreateExprFilter(Filter, FilterOptions, 0); + Check(Engine, Engine.ActivateFilter(FHandle, FExprFilter)); + end; + end; + if FFuncFilter <> nil then + begin + if (Engine.ActivateFilter(FHandle, FFuncFilter) <> DBIERR_NONE) then + begin + Engine.DropFilter(FHandle, FFuncFilter); + FFuncFilter := CreateFuncFilter(@TPSQLDataSet.RecordFilter, 1); + Check(Engine, Engine.ActivateFilter(FHandle, FFuncFilter)); + end; + end; +end; + +procedure TPSQLDataSet.DeactivateFilters; +begin + if FFuncFilter <> nil then Check(Engine, Engine.DeactivateFilter(FHandle, FFuncFilter)); + if FExprFilter <> nil then Check(Engine, Engine.DeactivateFilter(FHandle, FExprFilter)); +end; + +function TPSQLDataSet.CreateExprFilter(const Expr: string; + Options: TFilterOptions; Priority: Integer): HDBIFilter; +var + Parser: TExprParser; +begin + Parser := TExprParser.Create(Self, Expr, Options, [], '', nil, FldTypeMap); + try + Check(Engine, Engine.AddFilter(FHandle, 0, Priority, FALSE, PCANExpr(Parser.FilterData), nil, Result)); + finally + Parser.Free; + end; +end; + +function TPSQLDataSet.CreateFuncFilter(FilterFunc: Pointer;Priority: Integer): HDBIFilter; +begin + Check(Engine, Engine.AddFilter(FHandle, Integer(Self), Priority, FALSE, nil, PFGENFilter(FilterFunc), Result)); +end; + +function TPSQLDataSet.CreateLookupFilter(Fields: TList{$IFDEF NEXTGEN}{$ENDIF}; const Values: Variant; + Options: TLocateOptions; Priority: Integer): HDBIFilter; +var + I: Integer; + AFilter: TFilterExpr; + Expr, Node: PExprNode; + AFilterOptions: TFilterOptions; +begin + Node := nil; + Expr := nil; + if loCaseInsensitive in Options then + AFilterOptions := [foNoPartialCompare, foCaseInsensitive] + else + AFilterOptions := [foNoPartialCompare]; + AFilter := TFilterExpr.Create(Self, AFilterOptions, [], '', nil, FldTypeMap); + try + if (Fields.Count = 1) and not VarIsArray(Values) then + begin + Node := AFilter.NewCompareNode(TField(Fields[0]), coEQ, Values); + Expr := Node; + end + else + for I := 0 to Fields.Count-1 do + begin + Node := AFilter.NewCompareNode(TField(Fields[I]), coEQ, Values[I]); + if I = 0 then + Expr := Node else + Expr := AFilter.NewNode(enOperator, coAND, Unassigned, Expr, Node); + end; + if loPartialKey in Options then Node^.FPartial := TRUE; + Check(Engine, Engine.AddFilter(FHandle, 0, Priority, FALSE, PCANExpr(AFilter.GetFilterData(Expr)), nil, Result)); + finally + AFilter.Free; + end; +end; + +procedure TPSQLDataSet.SetFilterHandle(var Filter: HDBIFilter; Value: HDBIFilter); +begin + if Filtered then + begin + CursorPosChanged; + Engine.SetToBegin(FHandle); + if Filter <> nil then Engine.DropFilter(FHandle, Filter); + Filter := Value; + if Filter <> nil then Engine.ActivateFilter(FHandle, Filter); + end else + begin + if Filter <> nil then Engine.DropFilter(FHandle, Filter); + Filter := Value; + end; +end; + +procedure TPSQLDataSet.SetFilterData(const Text: string; Options: TFilterOptions); +var + HFilter: HDBIFilter; +begin + if Active then + begin + CheckBrowseMode; + if (Filter <> Text) or (FilterOptions <> Options) then + begin + if Text <> '' then + HFilter := CreateExprFilter(Text, Options, 0) else + HFilter := nil; + SetFilterHandle(FExprFilter, HFilter); + end; + end; + Inherited SetFilterText(Text); + Inherited SetFilterOptions(Options); + if Active and Filtered then First; +end; + +procedure TPSQLDataSet.SetFilterText(const Value: string); +begin + SetFilterData(Value, FilterOptions); +end; + +procedure TPSQLDataSet.SetFiltered(Value: Boolean); +begin + if Active then + begin + CheckBrowseMode; + if Filtered <> Value then + begin + Engine.SetToBegin(FHandle); + if Value then + ActivateFilters + else + DeactivateFilters; + inherited SetFiltered(Value); + end; + First; + end + else + inherited SetFiltered(Value); +end; + +procedure TPSQLDataSet.SetFilterOptions(Value: TFilterOptions); +begin + SetFilterData(Filter, Value); +end; + +procedure TPSQLDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent); +var + AFilter: HDBIFilter; +begin + if Active then + begin + CheckBrowseMode; + if Assigned(OnFilterRecord) <> Assigned(Value) then + begin + if Assigned(Value) then + AFilter := CreateFuncFilter(@TPSQLDataSet.RecordFilter, 1) else + AFilter := nil; + SetFilterHandle(FFuncFilter, AFilter); + end; + Inherited SetOnFilterRecord(Value); + if Filtered then + First; + end + else + Inherited SetOnFilterRecord(Value); +end; + +function TPSQLDataSet.FindRecord(Restart, GoForward: Boolean): Boolean; +var + Status: Word; +begin + CheckBrowseMode; + DoBeforeScroll; + SetFound(FALSE); + UpdateCursorPos; + CursorPosChanged; + if not Filtered then ActivateFilters; + try + if GoForward then + begin + if Restart then Check(Engine, Engine.SetToBegin(FHandle)); + Status := Engine.GetNextRecord(FHandle, dbiNoLock, nil, nil); + end + else + begin + if Restart then Check(Engine, Engine.SetToEnd(FHandle)); + Status := Engine.GetPriorRecord(FHandle, dbiNoLock, nil, nil); + end; + finally + if not Filtered then + DeactivateFilters; + end; + if Status = DBIERR_NONE then + begin + Resync([rmExact, rmCenter]); + SetFound(TRUE); + end; + Result := Found; + if Result then DoAfterScroll; +end; + +function TPSQLDataSet.RecordFilter(RecBuf: Pointer; RecNo: Integer): Smallint; +var + Accept: Boolean; + SaveState: TDataSetState; +begin + SaveState := SetTempState(dsFilter); + FFilterBuffer := RecBuf; + try + Accept := TRUE; + OnFilterRecord(Self, Accept); + except + {$IFNDEF FPC} + InternalHandleException(); + {$ENDIF} + end; + RestoreState(SaveState); + Result := Ord(Accept); +end; + +function TPSQLDataSet.LocateRecord(const KeyFields: string; + const KeyValues: Variant; + Options: TLocateOptions; + SyncCursor: Boolean): Boolean; +var + Fields: TList{$IFDEF DELPHI_17}{$ENDIF}; + CaseInsensitive: boolean; + Flds : array of integer; + SFlds : array of string; + i, FieldCount, R : integer; + aPartial : boolean; + Status : Word; +begin + if Self.Filtered then + begin + //mi:2009-07-31 we have to respect filters + Status := LocateFilteredRecord(KeyFields, KeyValues, Options, SyncCursor); + Result := Status = DBIERR_NONE; + Exit; + end; + + CheckBrowseMode(); + CursorPosChanged(); + DoBeforeScroll(); + + Result := False; + + Fields := TList{$IFDEF DELPHI_17}{$ENDIF}.Create; + try + GetFieldList(Fields, KeyFields); + CaseInsensitive := loCaseInsensitive in Options; + + FieldCount := Fields.Count; + + SetLength(Flds, FieldCount); + SetLength(SFlds, FieldCount); + + if FieldCount = 1 then + begin + Flds[0] := TField(Fields.First).FieldNo - 1; + if VarIsArray(KeyValues) then + SFlds[0] := VarToStr(KeyValues[0]) //mi:2009-12-22 #1270 thanks to Matija Vidmar + else + SFlds[0] := VarToStr(KeyValues); + end + else + for i := 0 to FieldCount - 1 do + begin + Flds[i] := TField(Fields[i]).FieldNo - 1; + SFlds[i] := VarToStr(KeyValues[i]) + end; + + aPartial := (loPartialKey in Options) and (TField(Fields.Last).DataType in [ftString, ftWideString]); + + R := TNativeDataSet(FHandle).FindRows(Flds, SFlds, not CaseInsensitive, 0, not aPartial); + + if R <> -1 then + begin + Result := True; + TNativeDataSet(FHandle).InitRecord(Pointer(ActiveBuffer)); + TNativeDataSet(FHandle).SetToRecord(R); + if SyncCursor then + Resync([rmExact, rmCenter]); + DoAfterScroll(); + end; + finally + Fields.Free(); + end; + +end; + +function TPSQLDataSet.LocateFilteredRecord(const KeyFields: string; + const KeyValues: Variant; + Options: TLocateOptions; + SyncCursor: Boolean): Word; +var + Fields: TList{$IFDEF DELPHI_17}{$ENDIF}; + AFilter: HDBIFilter; + Status: DBIResult; + I: Integer; + Filter1: TFilterExpr; + Expr, Node: PExprNode; + fo: TFilterOptions; + pos : int64; +begin + CheckBrowseMode(); + CursorPosChanged(); + DoBeforeScroll(); + + pos := TNativeDataSet(FHandle).RecordNumber; + + Fields := TList{$IFDEF DELPHI_17}{$ENDIF}.Create(); + try + GetFieldList(Fields, KeyFields); + Check(Engine, Engine.SetToBegin(FHandle)); + + fo := [foNoPartialCompare]; + + //mi:2010-02-07 + if loCaseInsensitive in Options then + fo := fo + [foCaseInsensitive]; + + Filter1 := TFilterExpr.Create(Self, fo, [], '', nil, FldTypeMap); + + try + Node := nil; + Expr := nil; + if Fields.Count = 1 then + begin + if VarIsArray(KeyValues) then + Node := Filter1.NewCompareNode(TField(Fields[0]), coEQ, KeyValues[0]) + else + Node := Filter1.NewCompareNode(TField(Fields[0]), coEQ, KeyValues); + + Expr := Node; + end + else + begin + for i := 0 to Fields.Count - 1 do + begin + Node := Filter1.NewCompareNode(TField(Fields[I]), coEQ, KeyValues[I]); + + if I = 0 then + Expr := Node + else + Expr := Filter1.NewNode(enOperator, coAND, Unassigned, Expr, Node); + end; + end; + + if loPartialKey in Options then + Node^.FPartial := TRUE; + + Check(Engine, Engine.AddFilter(FHandle, 0, 2, FALSE, PCANExpr(Filter1.GetFilterData(Expr)), nil, AFilter)); + finally + Filter1.Free(); + end; + + Engine.ActivateFilter(FHandle, AFilter); + Status := Engine.GetNextRecord(FHandle, dbiNoLock, Pointer(ActiveBuffer), nil); + Engine.DropFilter(FHandle, AFilter); + finally + Fields.Free(); + end; + + Result := Status; + + if SyncCursor then + begin + if Result = DBIERR_NONE then + begin + Resync([rmExact, rmCenter]); + end + else + begin +// TNativeDataSet(FHandle).InitRecord(ActiveBuffer); + TNativeDataSet(FHandle).SetToRecord(pos); + end; + + DoAfterScroll(); + end; +end; + +function TPSQLDataSet.LocateNearestRecord(const KeyFields: string;const KeyValues: Variant;Options: TLocateOptions;SyncCursor: Boolean): Word; +var + Buffer: TRecordBuffer; + Fields: TList{$IFDEF DELPHI_17}{$ENDIF}; + AFilter: HDBIFilter; + Status: DBIResult; + I: Integer; + Filter1: TFilterExpr; + Expr, Node: PExprNode; + AFilterOptions: TFilterOptions; + +begin + Expr := nil; //make compiler happy + Node := nil; //make compiler happy + CheckBrowseMode; + CursorPosChanged; + Buffer := Pointer(TempBuffer); + Fields := TList{$IFDEF DELPHI_17}{$ENDIF}.Create; + try + GetFieldList(Fields, KeyFields); + Check(Engine, Engine.SetToBegin(FHandle)); + AFilterOptions := [foNoPartialCompare]; + Filter1 := TFilterExpr.Create(Self, AFilterOptions, [], '', nil, FldTypeMap); + try + if Fields.Count = 1 then + begin + Node := Filter1.NewCompareNode(TField(Fields[0]), coGE, KeyValues); + Expr := Node; + end + else + for I := 0 to Fields.Count-1 do + begin + Node := Filter1.NewCompareNode(TField(Fields[I]), coGE, KeyValues[I]); + if I = 0 then + Expr := Node else + Expr := Filter1.NewNode(enOperator, coAND, Unassigned, Expr, Node); + end; + if loPartialKey in Options then Node^.FPartial := TRUE; + Check(Engine, Engine.AddFilter(FHandle, 0, 2, FALSE, PCANExpr(Filter1.GetFilterData(Expr)), nil,AFilter)); + finally + Filter1.Free; + end; + Engine.ActivateFilter(FHandle, AFilter); + Status := Engine.GetNextRecord(FHandle, dbiNoLock, Buffer, nil); + Engine.DropFilter(FHandle, AFilter); + finally + Fields.Free; + end; + Result := Status; +end; + +function IsSameVarArrays(A1, A2: variant): boolean; +var I: integer; +begin + Result := VarIsArray(A1) and VarIsArray(A2); + for I := VarArrayLowBound(A1, 1) to VarArrayHighBound(A2, 1) do + Result := Result AND (A1[i] = A2[i]); +end; + +function TPSQLDataSet.Lookup(const KeyFields: string; const KeyValues: Variant; + const ResultFields: string): Variant; +var OldPos: integer; + FVal: variant; +begin + Result := Null; + if VarIsNull(KeyValues) then Exit; + OldPos := RecNo; + DisableControls; + try + First; + while not Eof do + begin + FVal := FieldValues[KeyFields]; + if VarIsArray(FVal) and VarIsArray(KeyValues) then + begin + if IsSameVarArrays(FVal, KeyValues) then + Result := FieldValues[ResultFields] + end + else + if FVal = KeyValues then Result := FieldValues[ResultFields]; + if not VarIsNull(Result) then Exit; + Next; + end; + finally + if OldPos = 1 then First() else + if OldPos = RecordCount then Last() else RecNo := OldPos; + EnableControls; + end; +end; + +function TPSQLDataSet.Locate(const KeyFields: string; + const KeyValues: Variant; Options: TLocateOptions): Boolean; +begin + DoBeforeScroll(); + Result := LocateRecord(KeyFields, KeyValues, Options, True); +end; + +procedure TPSQLDataSet.AllocCachedUpdateBuffers(Allocate: Boolean); +begin + if Allocate then + begin + FUpdateCBBuf := AllocMem(SizeOf(DELAYUPDCbDesc)); + FUpdateCBBuf.pNewRecBuf := AllocMem(FRecBufSize); + FUpdateCBBuf.pOldRecBuf := AllocMem(FRecBufSize); + FUpdateCBBuf.iRecBufSize := FRecordSize; + end + else + begin + if Assigned(FUpdateCBBuf) then + begin + FreeMem(FUpdateCBBuf.pNewRecBuf); + FreeMem(FUpdateCBBuf.pOldRecBuf); + DisposeMem(FUpdateCBBuf, SizeOf(DELAYUPDCbDesc)); + end; + end; +end; + +procedure TPSQLDataSet.CheckCachedUpdateMode; +begin +end; + +function TPSQLDataSet.UpdateCallbackRequired: Boolean; +begin +{$IFDEF FPC} + Result := False; +{$ELSE} + Result := FCachedUpdates and (Assigned(FOnUpdateError) or + Assigned(FOnUpdateRecord) or Assigned(FUpdateObject)); +{$ENDIF} +end; + +function TPSQLDataSet.ForceUpdateCallback: Boolean; +begin + Result := True{FCachedUpdates} and ({$IFNDEF FPC}Assigned(FOnUpdateRecord) or{$ENDIF} + Assigned(FUpdateObject)); +end; + +procedure TPSQLDataSet.SetCachedUpdates(Value: Boolean); + + procedure ReAllocBuffers; + begin + FreeFieldBuffers; + FreeKeyBuffers; + SetBufListSize(0); + try + InitBufferPointers(TRUE); + SetBufListSize(BufferCount + 1); + AllocKeyBuffers; + except + SetState(dsInactive); + CloseCursor; + raise; + end; + end; + +begin + if (State = dsInActive) or (csDesigning in ComponentState) then + FCachedUpdates := Value + else + if (FCachedUpdates <> Value) then + begin + CheckBrowseMode; + UpdateCursorPos; + FCachedUpdates := Value; + ReAllocBuffers; + AllocCachedUpdateBuffers(Value); + SetupCallBack(UpdateCallBackRequired); + Resync([]); + end; +end; + +procedure TPSQLDataSet.SetupCallBack(Value: Boolean); +begin + if Value then + begin + if (csDesigning in ComponentState) then + Exit; + if not Assigned(FUpdateCallback) then + FUpdateCallback := TPSQLBDECallBack.Create(Engine, Self, Self.Handle, cbDELAYEDUPD, + FUpdateCBBuf, SizeOf(DELAYUPDCbDesc), CachedUpdateCallBack, TRUE); + end + else + begin + if Assigned(FUpdateCallback) then + begin + {$IFDEF NEXTGEN} + FUpdateCallback.DisposeOf; + {$ELSE} + FUpdateCallback.Free; + {$ENDIF} + FUpdateCallback := nil; + end; + end; +end; + +function TPSQLDataSet.ProcessUpdates(UpdCmd: DBIDelayedUpdCmd): Word; +begin + CheckCachedUpdateMode; + UpdateCursorPos; + Result :=0; +// Resync([]); //NEW +end; + +procedure TPSQLDataSet.ApplyUpdates; +var + Status: Word; +begin + if (State <> dsBrowse) then Post; + Status := ProcessUpdates(dbiDelayedUpdPrepare); + if (Status <> DBIERR_NONE) then + if (Status = DBIERR_UPDATEABORT) then SysUtils.Abort else TDbiError(Engine,Status); +end; + +procedure TPSQLDataSet.CommitUpdates; +begin + Check(Engine, ProcessUpdates(dbiDelayedUpdCommit)); +end; + +procedure TPSQLDataSet.CancelUpdates; +begin + Cancel; + ProcessUpdates(dbiDelayedUpdCancel); +end; + +procedure TPSQLDataSet.RevertRecord; +var + Status: Word; +begin + if State in dsEditModes then Cancel; + Status := ProcessUpdates(dbiDelayedUpdCancelCurrent); + if not ((Status = DBIERR_NONE) or (Status = DBIERR_NOTSUPPORTED)) then + Check(Engine, Status); +end; + + +function TPSQLDataSet.UpdateStatus: TUpdateStatus; +begin + Result := usUnModified; +end; + +function TPSQLDataSet.CachedUpdateCallBack(CBInfo: Pointer): CBRType; +const + CBRetCode: array[TUpdateAction] of CBRType = (cbrAbort, cbrAbort, + cbrSkip, cbrRetry, cbrPartialAssist); +var + UpdateAction: TUpdateAction; + UpdateKind: TUpdateKind; +begin + FInUpdateCallBack := TRUE; + UpdateAction := uaFail; + UpdateKind := TUpdateKind(ord(FUpdateCBBuf.eDelayUpdOpType)-1); + try +{$IFNDEF FPC} + if Assigned(FOnUpdateRecord) then + FOnUpdateRecord(Self, UpdateKind, UpdateAction) + else +{$ENDIF} + if Assigned(FUpdateObject) then + begin + FUpdateObject.Apply(UpdateKind); + UpdateAction := uaApplied; + end + else + TDbiError(Engine, FUpdateCBBuf.iErrCode); + except + on E: Exception do + begin + if E is EPSQLDatabaseError then + FUpdateCBBuf.iErrCode := EPSQLDatabaseError(E).ErrorCode; +{$IFNDEF FPC} + if (E is EDatabaseError) and Assigned(FOnUpdateError) then + FOnUpdateError(Self, EDatabaseError(E), UpdateKind, UpdateAction) + else +{$ENDIF} + begin + {$IFNDEF FPC} + InternalHandleException(); + {$ENDIF} + UpdateAction := uaAbort; + end; + end; + end; + Result := CBRetCode[UpdateAction]; + if UpdateAction = uaAbort then + FUpdateCBBuf.iErrCode := DBIERR_UPDATEABORT; + FInUpdateCallBack := FALSE; +end; + +{$IFNDEF FPC} +function TPSQLDataSet.GetUpdateRecordSet: TUpdateRecordTypes; +begin + if Active then + begin + Result := TUpdateRecordTypes(Byte(GetIntProp(Engine, FHandle, + curDELAYUPDDISPLAYOPT))); + end + else + Result := []; +end; + +procedure TPSQLDataSet.SetUpdateRecordSet(RecordTypes: TUpdateRecordTypes); +begin + CheckBrowseMode; + UpdateCursorPos; + Check(Engine, Engine.SetEngProp(hDbiObj(Handle), curDELAYUPDDISPLAYOPT, Longint(Byte(RecordTypes)))); + Resync([]); +end; +{$ENDIF} + +procedure TPSQLDataSet.SetUpdateObject(Value: TPSQLSQLUpdateObject); +begin + if (Value <> FUpdateObject) then + begin + if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then + FUpdateObject.DataSet := nil; + FUpdateObject := Value; + if Assigned(FUpdateObject) then + begin + { if another dataset already references this updateobject, then + remove the reference } + if Assigned(FUpdateObject.DataSet) and (FUpdateObject.DataSet <> Self) then + FUpdateObject.DataSet.UpdateObject := nil; + FUpdateObject.DataSet := Self; + end; + end; +end; + +{$IFNDEF FPC} +procedure TPSQLDataSet.SetOnUpdateError(UpdateEvent: TUpdateErrorEvent); +begin + if Active then SetupCallback(UpdateCallBackRequired); + FOnUpdateError := UpdateEvent; +end; +{$ENDIF} + +function TPSQLDataSet.GetUpdatesPending: Boolean; +begin + Result := GetIntProp(Engine, FHandle, curDELAYUPDNUMUPDATES) > 0; +end; + +//{$IFDEF DELPHI_17} +//procedure TPSQLDataSet.DataConvert(Field: TField; Source: TValueBuffer; {$IFDEF DELPHI_18}var{$ENDIF} Dest: TValueBuffer; ToNative: Boolean); +//begin +// if (Field.DataType = ftDateTime) and not ToNative then //#1871 TDateTimeField supports dates before 30/12/1899 from now +// Move(Source[0], Dest[0], SizeOf(TDateTime)) +// else +// inherited; +//end; +//{$ELSE} +//procedure TPSQLDataSet.DataConvert(Field: TField; Source, Dest: Pointer; ToNative: Boolean); +//begin +// if (Field.DataType = ftDateTime) and not ToNative then //#1871 TDateTimeField supports dates before 30/12/1899 from now +// TDateTime(Dest^) := TDateTime(Source^) +// else +// inherited; +//end; +//{$ENDIF} + +procedure TPSQLDataSet.DataEvent(Event: TDataEvent; Info: TDataEventInfo); + + procedure CheckIfParentScrolled; + var + ParentPosition, I: Integer; + begin + ParentPosition := 0; + with FParentDataSet do + if not IsEmpty then + for I := 0 to BookmarkSize - 1 do + ParentPosition := ParentPosition + Byte(TRecordBuffer(ActiveBuffer)[FBookmarkOfs+I]); + if (FLastParentPos = 0) or (ParentPosition <> FLastParentPos) then + begin + First; + FLastParentPos := ParentPosition; + end + else + begin + UpdateCursorPos; + Resync([]); + end; + end; + +begin + if (Event = deParentScroll) then + CheckIfParentScrolled; + inherited DataEvent(Event, Info); +end; + +{$IFNDEF FPC} +{ TBDEDataSet.IProviderSupport} +function TPSQLDataSet.PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; +var + PrevErr: Integer; +begin + if E is EPSQLDatabaseError then + begin + if Prev <> nil then + PrevErr := Prev.ErrorCode else + PrevErr := 0; + with EPSQLDatabaseError(E) do + Result := EUpdateError.Create(E.Message, '', ErrorCode, PrevErr, E); + end + else + Result := inherited PSGetUpdateException(E, Prev); +end; + +function TPSQLDataSet.PSIsSQLSupported: Boolean; +begin + Result := TRUE; +end; + +procedure TPSQLDataSet.PSReset; +begin + inherited PSReset; + if Handle <> nil then + Engine.ForceReread(Handle); +end; +{$ENDIF} + +function TPSQLDataSet.GetHandle: HDBICur; +begin + Result := FHandle; +end; + + +function TPSQLDataSet.CheckOpen(Status: Word): Boolean; +begin + case Status of + DBIERR_NONE: Result := TRUE; + DBIERR_NOTSUFFTABLERIGHTS: Result := FALSE; + else + TDbiError(Engine, Status); + Result := FALSE; + end; +end; + +procedure TPSQLDataSet.Disconnect; +begin + Close; +end; + +function TPSQLDataSet.GetDBHandle: DAChDBIDb; +begin + if FDatabase <> nil then + begin + if FDatabase.Handle = nil then + FDatabase.Connected := True; + Result := FDatabase.Handle; + end + else + Result := nil; +end; + +procedure TPSQLDataSet.GetDatabaseNames(List : TStrings); +var + i : Integer; + Names : TStringList; +begin + Names := TStringList.Create; + try + Names.Sorted := TRUE; + for I := 0 to DBList.Count-1 do + with TPSQLDatabase(DBList[i]) do Names.Add(DatabaseName); + List.Assign(Names); + finally + Names.Free; + end; +end; + +procedure TPSQLDataSet.CloseDatabase(Database: TPSQLDatabase); +begin + if Assigned(Database) then + Database.CloseDatabase(Database); +end; + +function TPSQLDataSet.SetDBFlag(Flag: Integer; Value: Boolean): Boolean; +begin + Result := Flag in DBFlags; + if Value then + begin + if not Result then + begin + if FDBFlags = [] then + begin + FDatabase.Open; + Inc(FDatabase.FRefCount); + {$IFNDEF FPC} + FDatabase.RegisterClient(Self); + {$ENDIF} + end; + Include(FDBFlags, Flag); + end; + end + else + begin + if Result then + begin + Exclude(FDBFlags, Flag); + if FDBFlags = [] then + begin + {$IFNDEF FPC} + FDatabase.UnRegisterClient(Self); + {$ENDIF} + CloseDatabase(FDatabase); + end; + end; + end; +end; + +procedure TPSQLDataSet.SetUpdateMode(const Value: TUpdateMode); +begin + if (FHandle <> nil) and True and CanModify then + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle), curUPDLOCKMODE, Longint(Value))); + FUpdateMode := Value; +end; + +{ AutoRefresh } +procedure TPSQLDataSet.SetAutoRefresh(const Value: Boolean); +begin + CheckInactive; + FAutoRefresh := Value; +end; + +procedure TPSQLDataSet.SetDatabase(Value: TPSQLDatabase); +begin + if Active then Close; + try + {$IFNDEF FPC} + if Assigned(FDatabase) then FDatabase.UnRegisterClient(Self); + {$ENDIF} + if Assigned(Value) then FDatabase := Value; + finally + FDatabase := Value; + end; +end; + +function TPSQLDataSet.GetDatabase: TPSQLDatabase; +begin + Result := TPSQLDatabase(FDatabase); +end; + +{$IFNDEF FPC} +procedure TPSQLDataSet.SetupAutoRefresh; +const + PropFlags : array[TAutoRefreshFlag] of LongInt = (0, curFIELDISAUTOINCR, curFIELDISDEFAULT); +var + I : Integer; + ColDesc : ServerColDesc; +begin + if AutoRefresh then + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),curAUTOREFETCH,LongInt(TRUE))); + for I := 0 to Fields.Count - 1 do + with Fields[I] do + if (AutoGenerateValue <> arNone) then + begin + ColDesc.iFldNum := I + 1; + ColDesc.bServerCol := TRUE; + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle), PropFlags[ AutoGenerateValue ], LongInt(@ColDesc))); + end; +end; + +{ TPSQLDataSet.IProviderSupport } +procedure TPSQLDataSet.PSGetAttributes(List: {$IFDEF DELPHI_19}TPacketAttributeList{$ELSE}TList{$ENDIF}); +var + Attr: {$IFNDEF NEXTGEN}PPacketAttribute{$ELSE}TPacketAttribute{$ENDIF}; +begin + inherited PSGetAttributes(List); //29.11.2007 + {$IFNDEF NEXTGEN} + New(Attr); + {$ENDIF} + List.Add(Attr); + {$IFNDEF NEXTGEN} + with Attr^ do + {$ELSE} + with Attr do + {$ENDIF} + begin + Name := 'LCID'; + Value := Integer(-1); + IncludeInDelta := False; + end; +end; + +function TPSQLDataSet.PSIsSQLBased: Boolean; +var + InProvider : Boolean; +begin + InProvider := SetDBFlag(dbfProvider, TRUE); + try + Result := True; + finally + SetDBFlag(dbfProvider, InProvider); + end; +end; + +function TPSQLDataSet.PSGetQuoteChar: string; +begin + Result := '"'; +end; + +function TPSQLDataSet.PSInTransaction: Boolean; +var + InProvider: Boolean; +begin + if not Assigned(Database) or not Database.Connected then + Result := FALSE + else + begin + InProvider := SetDBFlag(dbfProvider, TRUE); + try + Result := Database.InTransaction; + finally + SetDBFlag(dbfProvider, InProvider); + end; + end; +end; + +procedure TPSQLDataSet.PSStartTransaction; +begin + SetDBFlag(dbfProvider, TRUE); + try + if not PSIsSQLBased then + Database.TransIsolation := tiDirtyRead; + Database.StartTransaction; + except + SetDBFlag(dbfProvider, FALSE); + Raise; + end; +end; + +procedure TPSQLDataSet.PSEndTransaction(Commit : Boolean); +const + EndType: array[Boolean] of eXEnd = (xendABORT, xendCOMMIT); +begin + try + Database.ClearStatements; + Database.EndTransaction(EndType[ Commit ]); + finally + SetDBFlag(dbfProvider, FALSE); + end; +end; + +{$IFDEF DELPHI_17} +function TPSQLDataSet.PSExecuteStatement(const ASQL: string; + AParams: TParams): Integer; +var + InProvider: Boolean; +begin + InProvider := SetDBFlag(dbfProvider, True); + try + Result := Database.Execute(ASQL, AParams); + finally + SetDBFlag(dbfProvider, InProvider); + end; +end; + +function TPSQLDataSet.PSExecuteStatement(const ASQL: string; AParams: TParams; + var ResultSet: TDataSet): Integer; +var + InProvider: Boolean; +begin + Result := -1; + InProvider := SetDBFlag(dbfProvider, TRUE); + try + ResultSet := TPSQLQuery.Create(nil); + try + TPSQLQuery(ResultSet).Database := Database; + TPSQLQuery(ResultSet).SQL.Text := ASQL; + TPSQLQuery(ResultSet).Params.Assign(AParams); + TPSQLQuery(ResultSet).Open; + Result := Max(TPSQLQuery(ResultSet).RowsAffected, TPSQLQuery(ResultSet).RecordCount); + except + FreeAndNil(ResultSet); + raise; + end; + finally + SetDBFlag(dbfProvider, InProvider); + end; +end; +{$ELSE} +function TPSQLDataSet.PSExecuteStatement(const ASQL : string; AParams: TParams; ResultSet: Pointer = nil): Integer; +var + InProvider: Boolean; + Q: TPSQLQuery; +begin + InProvider := SetDBFlag(dbfProvider, TRUE); + try + if Assigned(ResultSet) or Assigned(AParams) then + begin + {$WARNINGS OFF} //make D5 compiler happy + Q := TPSQLQuery.Create(nil); + {$WARNINGS ON} + try + Q.Database := Database; + Q.SQL.Text := ASQL; + Q.Params.Assign(AParams); + // In Insert, Resolver expect the number of records affected... + if not Assigned(ResultSet) then + Q.ExecSQL + else + Q.Open; + Result := Q.RowsAffected; + finally + if Assigned(ResultSet) then + TPSQLDataset(ResultSet^) := Q + else + Q.Free; + end; + end + else + Result := Database.Execute(ASQL); + finally + SetDBFlag(dbfProvider, InProvider); + end; +end; +{$ENDIF DELPHI_17} +{$ENDIF FPC} + +///////////////////////////////////////////////////////////////// +// TPSQLQuery // +///////////////////////////////////////////////////////////////// +constructor TPSQLQuery.Create(AOwner: TComponent); +begin + Inherited Create(AOwner); + FSQL := TStringList.Create; + TStringList(SQL).OnChange := QueryChanged; + FParams := TPSQLParams.Create(Self); + FDataLink := TPSQLQueryDataLink.Create(Self); + RequestLive := FALSE; + ParamCheck := TRUE; + FRowsAffected := -1; + CacheBlobs := False; +end; + +destructor TPSQLQuery.Destroy; +begin + Destroying; + Disconnect; + FSQL.Free; + FParams.Free; + FDataLink.Free; + StrDispose(SQLBinary); + Inherited Destroy; +end; + +function TPSQLQuery.Engine : TPSQLEngine; +begin + Result := FDataBase.Engine; +end; + +function TPSQLQuery.CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; +begin + Result := TPSQLBlobStream.Create(Field as TBlobField, Mode); +end; + +function TPSQLQuery.IsSequenced: Boolean; +begin + Result := FAllowSequenced and inherited IsSequenced; +end; + +procedure TPSQLQuery.Disconnect; +begin + Close; + UnPrepare; +end; + +procedure TPSQLQuery.SetPrepare(Value: Boolean); +begin + if Value then + Prepare else UnPrepare; +end; + +procedure TPSQLQuery.Prepare; +begin + if Assigned(FHandle) then + begin + SetDBFlag(dbfPrepared, TRUE); + SetPrepared(TRUE); + end; +end; + +procedure TPSQLQuery.UnPrepare; +begin + SetPrepared(FALSE); + SetDBFlag(dbfPrepared, FALSE); +end; + +procedure TPSQLQuery.SetDataSource(Value: TDataSource); +begin + if IsLinkedTo(Value) then + DatabaseError(SCircularDataLink, Self); + FDataLink.DataSource := Value; +end; + +function TPSQLQuery.GetDataSource: TDataSource; +begin + Result := FDataLink.DataSource; +end; + +procedure TPSQLQuery.SetQuery(Value: TStrings); +begin + if SQL.Text <> Value.Text then + begin + Disconnect; + SQL.BeginUpdate; + try + SQL.Assign(Value); + finally + SQL.EndUpdate; + end; + end; +end; + +function TPSQLQuery.GetQuery:TStrings; +begin + Result := FSQL; +end; + +procedure TPSQLQuery.QueryChanged(Sender: TObject); +var + List: TPSQLParams; +begin + if not (csReading in ComponentState) then + begin + Disconnect; + StrDispose(SQLBinary); + SQLBinary := nil; + if ParamCheck {or (csDesigning in ComponentState)} then + begin + List := TPSQLParams.Create(Self); + try + FText := List.ParseSQL(SQL.Text, True); + List.AssignValues(FParams); + FParams.Clear; + FParams.Assign(List); + finally + List.Free; + end; + end else + FText := SQL.Text; + DataEvent(dePropertyChange, 0); + end else + FText := FParams.ParseSQL(SQL.Text, False); +end; + +procedure TPSQLQuery.SetParamsList(Value: TPSQLParams); +begin + FParams.AssignValues(Value); +end; + +function TPSQLQuery.GetParamsCount: integer; +begin + Result := FParams.Count; +end; + +procedure TPSQLQuery.DefineProperties(Filer: TFiler); + + function WriteData: Boolean; + begin + if (Filer.Ancestor <> nil) then + Result := not FParams.IsEqual(TPSQLQuery(Filer.Ancestor).FParams) + else + Result := (FParams.Count > 0); + end; + +begin + Inherited DefineProperties(Filer); + Filer.DefineBinaryproperty('Data', ReadBinaryData, WriteBinaryData, SQLBinary <> nil); + Filer.DefineProperty('ParamData', ReadParamData, WriteParamData, WriteData); +end; + +procedure TPSQLQuery.ReadParamData(Reader: TReader); +begin + Reader.ReadValue; + Reader.ReadCollection(FParams); +end; + +procedure TPSQLQuery.WriteParamData(Writer: TWriter); +begin + Writer.WriteCollection(Params); +end; + +procedure TPSQLQuery.ReadBinaryData(Stream: TStream); +begin + SQLBinary := StrAlloc(Stream.Size); + Stream.ReadBuffer(SQLBinary^, Stream.Size); +end; + +procedure TPSQLQuery.WriteBinaryData(Stream: TStream); +begin + Stream.WriteBuffer(SQLBinary^, StrBufSize(SQLBinary)); +end; + +procedure TPSQLQuery.SetRequestLive(const Value : Boolean); +begin + if Value <> FRequestLive then + FRequestLive := Value; +end; + +function TPSQLQuery.GetRequestLive : Boolean; +begin + Result := FRequestLive; +end; + +procedure TPSQLQuery.SetPrepared(Value: Boolean); +begin + if (FHandle <> nil) and Value then + DatabaseError(SDataSetOpen, Self); + if Value <> Prepared then + begin + if Value then + begin + FRowsAffected := -1; + FCheckRowsAffected := TRUE; + if Length(Text) > 1 then + PrepareSQL(PChar(Text)) else + DatabaseError(SEmptySQLStatement, Self); + end + else + begin + if FCheckRowsAffected then + FRowsAffected := RowsAffected; + end; + FPrepared := Value; + end; +end; + +procedure TPSQLQuery.SetParamsFromCursor; +var + I: Integer; + DataSet: TDataSet; +begin + if FDataLink.DataSource <> nil then + begin + DataSet := FDataLink.DataSource.DataSet; + if DataSet <> nil then + begin + DataSet.FieldDefs.Update; + for I := 0 to FParams.Count - 1 do + if not FParams[I].Bound then + begin + FParams[I].AssignField(DataSet.FieldByName(FParams[I].Name)); + FParams[I].Bound := FALSE; + end; + end; + end; +end; + +procedure TPSQLQuery.RefreshParams; +var + DataSet: TDataSet; +begin + DisableControls; + try + if FDataLink.DataSource <> nil then + begin + DataSet := FDataLink.DataSource.DataSet; + if DataSet <> nil then + if DataSet.Active and (DataSet.State <> dsSetKey) then + begin + TNativeDataset(FHandle).CloseTable; + SetParamsFromCursor(); + if FParams.Count > 0 then + TNativeDataset(FHandle).QuerySetParams(FParams, FSQL.Text); + TNativeDataset(FHandle).OpenTable(); + First(); + end; + end; + finally + EnableControls; + end; +end; + + +function TPSQLQuery.ParamByName(const Value: string): TPSQLParam; +begin + Result := FParams.ParamByName(Value); +end; + +function TPSQLQuery.CreateCursor(GenHandle: Boolean): HDBICur; +begin + if SQL.Count > 0 then + begin + FExecSQL := not GenHandle; + try + SetPrepared(TRUE); + finally + FExecSQL := FALSE; + end; + if FDataLink.DataSource <> nil then SetParamsFromCursor; + Result := GetQueryCursor(GenHandle); + end + else + begin + DatabaseError(SEmptySQLStatement, Self); + Result := nil; + end; + FCheckRowsAffected := (Result = nil); +end; + + +function TPSQLQuery.CreateHandle: HDBICur; +begin + Result := CreateCursor(TRUE) +end; + + +procedure TPSQLQuery.ExecSQL; +begin + CheckInActive; + if Assigned(FBeforeExecSQL) then + FBeforeExecSQL(Self); + SetDBFlag(dbfExecSQL, TRUE); + try + CreateCursor(FALSE); + finally + SetDBFlag(dbfExecSQL, FALSE); + if FHandle <> nil then + begin + Check(Engine, Engine.CloseCursor(hDBICur(FHandle))); + FHandle := nil; + end; + end; +end; + +function TPSQLQuery.GetQueryCursor(GenHandle: Boolean): HDBICur; +const + DataType: array[Boolean] of LongInt = (Ord(wantCanned), Ord(wantLive)); +var + PCursor: phDBICur; + CanLive : boolean; +begin + Result := nil; + if GenHandle then + PCursor := @Result else + PCursor := nil; + if FParams.Count > 0 then + Check(Engine,Engine.QuerySetParams(hDBIStmt(FHandle),Params,SQL.Text)); + Check(Engine, Engine.QExec(hDBIStmt(FHandle), PCursor, FRowsAffected)); + //pasha_golub 20.12.06 + CanLive := False; + if FRequestLive and not ForceUpdateCallback and not FExecSQL then + CanLive := TNativeDataSet(FHandle).CheckCanLive(); + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle), stmtLIVENESS, DataType[CanLive])); + //pasha_golub 20.12.06 +end; + +function TPSQLQuery.SetDBFlag(Flag: Integer; Value: Boolean): Boolean; +var + NewConnection: Boolean; +begin + if Value then + begin + NewConnection := DBFlags = []; + Result := Inherited SetDBFlag(Flag, Value); + if not (csReading in ComponentState) and NewConnection then + FLocal := False; + end + else + begin + if DBFlags - [Flag] = [] then + SetPrepared(FALSE); + Result := Inherited SetDBFlag(Flag, Value); + end; +end; + +procedure TPSQLQuery.SetOptions(const Value: TPSQLDatasetOptions); +begin + if Value = FOptions then Exit; + inherited; + if Active then + begin + Close; + Open; + end; +end; + +procedure TPSQLQuery.PrepareSQL(Value: PChar); +begin + GetStatementHandle(Value); + if not Local then + SetBoolProp(Engine, FHandle, stmtUNIDIRECTIONAL, FUniDirectional); +end; + +procedure TPSQLQuery.GetStatementHandle(SQLText: PChar); +const + DataType: array[Boolean] of LongInt = (Ord(wantCanned), Ord(wantLive)); +var + DBh : DAChDBIDb; +begin + DBh := DBHandle; + Check(Engine,Engine.QAlloc(DBH, hDBIStmt(FHandle))); + try + TNativeDataset(FHandle).Options := Options; + if not FExecSQL then + begin + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),stmtLIVENESS, + DataType[RequestLive and not ForceUpdateCallback])); + end; + if Local then + begin + SetBoolProp(Engine,FHandle,stmtAUXTBLS,FALSE); + SetBoolProp(Engine,FHandle,stmtCANNEDREADONLY,TRUE); + end; + while not CheckOpen(Engine.QPrepare(hDBIStmt(FHandle), SQLText)) do + {Retry}; + except + Engine.QFree(hDBIStmt(FHandle)); + FHandle := nil; + raise; + end; +end; + + +function TPSQLQuery.GetRowsAffected: Integer; +var + Length: integer; +begin + if Prepared then + begin + if Engine.GetEngProp(HDBIObj(FHandle), stmtROWCOUNT, @Result, SizeOf(Result), Length) > DBIERR_NONE then + Result := -1; + end + else + Result := FRowsAffected; +end; + + +{$IFNDEF FPC} +procedure TPSQLQuery.GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF NEXTGEN}{$ENDIF}); + + function AddFieldToList(const FieldName: string; DataSet: TDataSet; + List: TList{$IFDEF NEXTGEN}{$ENDIF}): Boolean; + var + Field: TField; + begin + Field := DataSet.FindField(FieldName); + if (Field <> nil) then + List.Add(Field); + Result := Field <> nil; + end; + +var + i: Integer; +begin + MasterFields.Clear; + DetailFields.Clear; + if (DataSource <> nil) and (DataSource.DataSet <> nil) then + for i := 0 to Params.Count - 1 do + if AddFieldToList(Params[i].Name, DataSource.DataSet, MasterFields) then + AddFieldToList(Params[i].Name, Self, DetailFields); +end; + +{ TPSQLQuery.IProviderSupport } +function TPSQLQuery.PSGetDefaultOrder: TIndexDef; +begin + Result := inherited PSGetDefaultOrder; + if not Assigned(Result) then + Result := GetIndexForOrderBy(SQL.Text, Self); +end; + +function TPSQLQuery.PSGetParams : TParams; +begin + Result := FParams; +end; + +procedure TPSQLQuery.PSSetParams(AParams : TParams); +begin + if (AParams.Count <> 0) then + Params.Assign(AParams); + Close; +end; + +function TPSQLQuery.PSGetTableName: string; +begin + Result := GetTableNameFromSQL(SQL.Text); +end; + +procedure TPSQLQuery.PSExecute; +begin + ExecSQL; +end; + +procedure TPSQLQuery.PSSetCommandText(const CommandText : string); +begin + if (CommandText <> '') then + SQL.Text := CommandText; +end; +{$ENDIF} + +function TPSQLDataSet.GetLastInsertID(const FieldNum: integer): integer; +begin + CheckActive; + if Assigned(FHandle) then + Check(Engine, Engine.GetLastInsertId(FHandle, FieldNum, Result)) + else + Result := -1; +end; + +{$IFDEF DELPHI_12} +function TPSQLDataSet.GetLookupListClass(Field: TField): TLookupListClass; +begin + Result := TPSQLLookupList; +end; +{$ENDIF} + +function TPSQLDataSet.GetStmtHandle: HDBIStmt; +begin + Result := hDBIStmt(GetHandle()); +end; + +function TPSQLDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass; +begin + if (FieldType = ftGuid) and (dsoUseGUIDField in FOptions) then + Result := TPSQLGuidField + else + Result := inherited GetFieldClass(FieldType); +end; + +procedure TPSQLDataSet.SortBy(FieldNames: string); +begin + if Active and (RecordCount > 1) then + try + TNativeDataSet(FHandle).SortBy(FieldNames); + TNativeDataset(Fhandle).SetRowPosition(-1, -1, Pointer(ActiveBuffer)); + Resync([]); + except + FSortFieldNames := ''; + raise; + end; +end; + + +procedure TPSQLDataSet.SortBy(FieldNames: string; Compare: TPSQLDatasetSortCompare); +begin + if Active and (RecordCount > 1) then + begin + TNativeDataSet(FHandle).SortBy(FieldNames, Compare); + TNativeDataset(Fhandle).SetRowPosition(-1, -1, Pointer(ActiveBuffer)); + end; +end; + +procedure TPSQLDataSet.SetOptions(const Value: TPSQLDatasetOptions); +begin + if (dsoFetchOnDemand in Value) then + begin + if not (Self is TPSQLQuery) then DatabaseError('dsoFetchOnDemand option is applicable only to TPSQLQuery objects', Self); + if (Self as TPSQLQuery).RequestLive then DatabaseError('RequestLive must be False to apply dsoFetchOnDemand option', Self); + end; + FOptions := Value; +end; + +procedure TPSQLDataSet.SetSortFieldNames(const Value: string); +begin + if FSortFieldNames <> Value then + begin + SortBy(Value); + FSortFieldNames := Value; + end; +end; + +function TPSQLDataSet.GetSortFieldNames: string; +begin + if not Active or + not Assigned(FHandle) or + not TNativeDataSet(FHandle).IsSortedLocally then + FSortFieldNames := ''; + Result := FSortFieldNames; +end; + +function TPSQLDataSet.GetStoreActive: boolean; +begin + Result := Active + and Assigned(FDatabase) + and ( + (ddoStoreConnected in FDatabase.DesignOptions) + or not (csDesigning in ComponentState) + ); +end; + +function TPSQLDataSet.GetFieldTypeOID(const FieldNum: integer): cardinal; +begin + if Assigned(FHandle) then + Result := Engine.GetFieldTypeOID(FHandle, FieldNum) + else + Result := InvalidOID; +end; + +{ TPSQLUpdateSQL } +constructor TPSQLUpdateSQL.Create(AOwner: TComponent); +var + UpdateKind: TUpdateKind; +begin + Inherited Create(AOwner); + for UpdateKind := Low(TUpdateKind) to High(TUpdateKind) do + begin + FSQLText[UpdateKind] := TStringList.Create; + TStringList(FSQLText[UpdateKind]).OnChange := SQLChanged; + end; +end; + +destructor TPSQLUpdateSQL.Destroy; +var + UpdateKind: TUpdateKind; +begin + if Assigned(FDataSet) and (FDataSet.UpdateObject = Self) then + FDataSet.UpdateObject := nil; + for UpdateKind := Low(TUpdateKind) to High(TUpdateKind) do + FSQLText[UpdateKind].Free; + Inherited Destroy; +end; + +procedure TPSQLUpdateSQL.ExecSQL(UpdateKind: TUpdateKind); +var RN, RC: integer; +begin + with Query[UpdateKind] do + begin + Prepare; + ExecSQL; + if Assigned(FDataSet) then + begin + TNativeDataset(FDataset.Handle).FreeBlobStreams(Pointer(FDataset.ActiveBuffer)); //30.10.2012 + RN := TNativeDataset(FDataset.Handle).RecordNumber; + TNativeDataset(FDataset.Handle).OpenTable; + TNativeDataset(FDataset.Handle).RecordState := tsPos; + if UpdateKind <> ukDelete then + begin + if not TNativeDataset(FDataset.Handle).SetRowPosition(-1, 0, Pointer(FDataset.ActiveBuffer)) then + try + TNativeDataset(FDataset.Handle).SettoSeqNo(RN + 1); + except + end + end + else + begin + if Engine.GetRecordCount(FDataset.Handle, RC) <> DBIERR_NONE then + RC := -1; + if RN >= RC then + RN := 0; + try + TNativeDataset(FDataset.Handle).SettoSeqNo(RN); + except + end; + end; + TNativeDataset(FDataset.Handle).IsLocked := False; + end; + if Assigned(FRecordChangeCompleteEvent) then + FRecordChangeCompleteEvent(FDataset,UpdateKind); + end; +end; + +function TPSQLUpdateSQL.GetQueryClass : TPSQLQueryClass; +begin + Result := TPSQLQuery; +end; + +function TPSQLUpdateSQL.GetQuery(UpdateKind: TUpdateKind): TPSQLQuery; +begin + if not Assigned(FQueries[UpdateKind]) then + begin + FQueries[UpdateKind] := GetQueryClass.Create(Self); + FQueries[UpdateKind].SQL.Assign(FSQLText[UpdateKind]); + if FDataSet is TPSQLDataSet then + FQueries[UpdateKind].Database := TPSQLDataSet(FDataSet).DataBase; + end; + Result := FQueries[UpdateKind]; +end; + +function TPSQLUpdateSQL.GetSQL(UpdateKind: TUpdateKind): TStrings; +begin + Result := FSQLText[UpdateKind]; +end; + +function TPSQLUpdateSQL.GetSQLIndex(Index: Integer): TStrings; +begin + Result := FSQLText[TUpdateKind(Index)]; +end; + +function TPSQLUpdateSQL.GetDataSet: TPSQLDataSet; +begin + Result := FDataSet; +end; + +procedure TPSQLUpdateSQL.SetDataSet(ADataSet: TPSQLDataSet); +begin + FDataSet := ADataSet; +end; + +procedure TPSQLUpdateSQL.SetSQL(UpdateKind: TUpdateKind; Value: TStrings); +begin + FSQLText[UpdateKind].Assign(Value); +end; + +procedure TPSQLUpdateSQL.SetSQLIndex(Index: Integer; Value: TStrings); +begin + SetSQL(TUpdateKind(Index), Value); +end; + +procedure TPSQLUpdateSQL.SQLChanged(Sender: TObject); +var + UpdateKind: TUpdateKind; +begin + for UpdateKind := Low(TUpdateKind) to High(TUpdateKind) do + if Sender = FSQLText[UpdateKind] then + begin + if Assigned(FQueries[UpdateKind]) then + begin + FQueries[UpdateKind].Params.Clear; + FQueries[UpdateKind].SQL.Assign(FSQLText[UpdateKind]); + end; + Break; + end; +end; + +procedure TPSQLUpdateSQL.SetParams(UpdateKind: TUpdateKind); +var + I: Integer; + Old: Boolean; + Param: TPSQLParam; + PName: string; + Field: TField; +begin + if not Assigned(FDataSet) then Exit; + Query[UpdateKind].Database := FDataset.Database; //01.08.2008 + with Query[UpdateKind] do + begin + for I := 0 to Params.Count - 1 do + begin + Param := Params[I]; + PName := Param.Name; + Old := CompareText(Copy(PName, 1, 4), 'OLD_') = 0; + if Old and (UpdateKind in [ukInsert,ukDelete]) then + DatabaseError(Format(SNoParameterValue,[Param.Name])); + if Old then + System.Delete(PName, 1, 4); + Field := FDataSet.FindField(PName); + if not Assigned(Field) then Continue; + if Old then + Check(FDataset.Engine,FDataset.Engine.GetFieldOldValue(FDataset.Handle, PName, Param)) + else + Check(FDataset.Engine,FDataset.Engine.GetFieldValueFromBuffer(FDataset.Handle, Pointer(FDataset.ActiveBuffer), PName, Param, UpdateKind <> ukModify)); + if Param.DataType = ftUnknown then + Param.DataType := ftString; + end; + end; +end; + +procedure TPSQLUpdateSQL.Apply(UpdateKind: TUpdateKind); +begin + SetParams(UpdateKind); + ExecSQL(UpdateKind); +end; + +/////////////////////////////////////////////////////////////////////////////// +// TPSQLTable // +/////////////////////////////////////////////////////////////////////////////// +constructor TPSQLTable.Create(AOwner: TComponent); +begin + Inherited Create(AOwner); + FIndexDefs := TIndexDefs.Create(Self); + FMasterLink := TMasterDataLink.Create(Self); + FMasterLink.OnMasterChange := MasterChanged; + FMasterLink.OnMasterDisable := MasterDisabled; + FDefaultIndex := TRUE; + CacheBlobs := False; + FLimit := 0; +end; + +destructor TPSQLTable.Destroy; +begin + Inherited Destroy; + FMasterLink.Free; + FIndexDefs.Free; +end; + +function TPSQLTable.GetLimit: Integer; +begin + Result := FLimit; +end; + +procedure TPSQLTable.SetLimit(const Value : Integer); +begin + if FLimit <> Value then + FLimit := Value; +end; + +function TPSQLTable.GetHandle(const IndexName, IndexTag: string): HDBICur; +const + OpenModes: array[Boolean] of DbiOpenMode = (dbiReadWrite, dbiReadOnly); + ShareModes: array[Boolean] of DbiShareMode = (dbiOpenShared, dbiOpenExcl); +var + IndexID: Word; + OpenMode: DbiOpenMode; + RetCode: Word; + DBH : DAChDBIDb; + + procedure FillAddonProps; + begin + Check(Engine,Engine.GetTableProps(DBHandle, FTableName, FOwner, + FComment, FTablespace, FTableID)); + end; + +begin + Result := nil; + OpenMode := OpenModes[FReadOnly]; + if DefaultIndex then + IndexID := 0 + else + IndexID := NODEFAULTINDEX; + while TRUE do + begin + DBH := DBHandle; + RetCode := Engine.OpenTable(DBH, FTableName, IndexName, IndexID, OpenMode, ShareModes[FExclusive], + Result, FOptions, FLimit, FOffset); + if RetCode = DBIERR_TABLEREADONLY then + OpenMode := dbiReadOnly + else + FillAddonProps(); + if CheckOpen(RetCode) then Break; + end; +end; + +function TPSQLTable.Engine : TPSQLEngine; +begin + Result := FDataBase.Engine; +end; + +function TPSQLTable.CreateBlobStream(Field : TField; Mode : TBlobStreamMode) : TStream; +begin + Result := TPSQLBlobStream.Create(Field as TBlobField, Mode); +end; + +function TPSQLTable.IsSequenced: Boolean; +begin + Result := FAllowSequenced and inherited IsSequenced; +end; + +function TPSQLTable.CreateHandle: HDBICur; +var + AnIndexName, IndexTag: string; +begin + if FTableName = '' then DatabaseError(SNoTableName, Self); + IndexDefs.Updated := FALSE; + GetIndexParams(FIndexName, FFieldsIndex, AnIndexName, IndexTag); + Result := GetHandle(AnIndexName, IndexTag); + TNativeDataset(Result).Options := Options; +end; + +function TPSQLTable.GetLanguageDriverName: string; +begin + Result := ''; +end; + +procedure TPSQLTable.PrepareCursor; +begin + CheckMasterRange; +end; + +{$IFNDEF FPC} +procedure TPSQLTable.DefChanged(Sender: TObject); +begin + StoreDefs := TRUE; +end; +{$ENDIF} + +procedure TPSQLTable.InitFieldDefs; +var + I, FieldID, FldDescCount: Integer; + FieldDescs: TFLDDescList; + FCursor: HDBICur; + RequiredFields: TBits; +begin + if FHandle <> nil then + InternalInitFieldDefs else + begin + SetDBFlag(dbfFieldList, TRUE); + try + if (FTableName = '') then DatabaseError(SNoTableName, Self); + while not CheckOpen(Engine.OpenFieldList(DBHandle, FTableName, + '', FALSE, FCursor)) do {Retry}; + try + Check(Engine, Engine.GetRecordCount(FCursor, FldDescCount)); + SetLength(FieldDescs, FldDescCount); + { Create an array of field descriptors } + for I := 0 to FldDescCount - 1 do + Check(Engine, Engine.GetNextRecord(FCursor, dbiNoLock, @FieldDescs[I], nil)); + { Initialize list of required fields } + RequiredFields := TBits.Create; + try + if FieldDescs[FldDescCount-1].iFldNum > FldDescCount then + RequiredFields.Size := FieldDescs[FldDescCount-1].iFldNum + 1 + else + RequiredFields.Size := FldDescCount + 1; + { Initialize the FieldDefs } + FieldDefs.BeginUpdate; + try + FieldDefs.Clear; + I := 0; + FieldID := 1; + while I < FldDescCount do + AddFieldDesc(FieldDescs, I, FieldID, RequiredFields, FieldDefs); + finally + FieldDefs.EndUpdate; + end; + finally + RequiredFields.Free; + end; + finally + Engine.CloseCursor(FCursor); + end; + finally + SetDBFlag(dbfFieldList, FALSE); + end; + end; +end; + +procedure TPSQLTable.DestroyHandle; +begin + inherited DestroyHandle; +end; + +procedure TPSQLTable.DecodeIndexDesc(const IndexDesc: IDXDesc; + var Source, Name, FieldExpression, DescFields: string; + var Options: TIndexOptions); + + procedure ConcatField(var FieldList: string; const FieldName: string); + begin + if FieldList = '' then + FieldList := FieldName else + FieldList := Format('%s;%s', [FieldList, FieldName]); + end; + +var + IndexOptions: TIndexOptions; + I: Integer; + FieldName: string; + s : string; +begin + with IndexDesc do + begin + S := szName; + Name := ExtractFileName(string(s)); + Source := ExtractFileName(Source); + IndexOptions := []; + if bPrimary then Include(IndexOptions, ixPrimary); + if bUnique then Include(IndexOptions, ixUnique); + if bDescending then Include(IndexOptions, ixDescending); + if bCaseInsensitive then Include(IndexOptions, ixCaseInsensitive); + if not bMaintained then Include(IndexOptions, ixNonMaintained); + if bExpIdx then + begin + //TNativeToAnsi(Engine, szKeyExp, S); + S := szKeyExp; + FieldExpression := string(s); + Include(IndexOptions, ixExpression); + end else + begin + FieldExpression := ''; + DescFields := ''; + for I := 0 to iFldsInKey - 1 do + begin + FieldName := FieldDefs[aiKeyFld[I] - 1].Name; + ConcatField(FieldExpression, FieldName); + if abDescending[I] then + ConcatField(DescFields, FieldName); + end; + if bDescending and (DescFields = FieldExpression) then DescFields := ''; + end; + Options := IndexOptions; + end; +end; + +procedure TPSQLTable.EncodeIndexDesc(var IndexDesc: IDXDesc; + const Name, FieldExpression: string; Options: TIndexOptions; + const DescFields: string); + + function IndexFieldOfs(const FieldName: string): Integer; + var + FieldNo: Integer; + begin + FieldNo := FieldDefs.Find(FieldName).FieldNo; + for Result := 0 to IndexDesc.iFldsInKey - 1 do + if IndexDesc.aiKeyFld[Result] = FieldNo then + Exit; + DatabaseErrorFmt(SIndexFieldMissing, [FieldName], Self); + Result := -1; + end; + +var + Pos: Integer; +begin + FillChar(IndexDesc, SizeOf(IndexDesc), 0); + with IndexDesc do + begin + //Move(Name[{$IFNDEF NEXTGEN}1{$ELSE}0{$ENDIF}], szName, Max(Length(Name), DBIMAXNAMELEN) * SizeOf(Char)); + IndexDesc.szName := Name; + bPrimary := ixPrimary in Options; + bUnique := ixUnique in Options; + bDescending := (ixDescending in Options) and (DescFields = ''); + bMaintained := not (ixNonMaintained in Options); + Word(bCaseInsensitive) := Word(ixCaseInsensitive in Options); + if ixExpression in Options then + begin + bExpIdx := TRUE; + //TAnsiToNative(Engine, FieldExpression, szKeyExp, SizeOf(szKeyExp) - 1); + szKeyExp := FieldExpression; + end + else + begin + Pos := 1; + while (Pos <= Length(FieldExpression)) and (iFldsInKey < DBIMAXFLDSINKEY) do + begin + aiKeyFld[iFldsInKey] := + FieldDefs.Find(ExtractFieldName(FieldExpression, Pos)).FieldNo; + Inc(iFldsInKey); + end; + if (DescFields <> '') then + begin + bDescending := TRUE; + Pos := 1; + while Pos <= Length(DescFields) do + abDescending[IndexFieldOfs(ExtractFieldName(DescFields, Pos))] := TRUE; + end; + end; + end; +end; + +procedure TPSQLTable.AddIndex(const Name, Fields: string; Options: TIndexOptions; + const DescFields: string); +var + IndexDesc: IDXDesc; +begin + FieldDefs.Update; + if Active then + begin + EncodeIndexDesc(IndexDesc, Name, Fields, Options, DescFields); + CheckBrowseMode; + CursorPosChanged; + Check(Engine, Engine.AddIndex(DBHandle, Handle, '', '', IndexDesc, '')); + end + else + begin + EncodeIndexDesc(IndexDesc, Name, Fields, Options, DescFields); + SetDBFlag(dbfTable, TRUE); + try + Check(Engine, Engine.AddIndex(DBHandle, nil, FTableName, '', IndexDesc, '')); + finally + SetDBFlag(dbfTable, FALSE); + end; + end; + IndexDefs.Updated := FALSE; +end; + +procedure TPSQLTable.DeleteIndex(const Name: string); +var + AnIndexName, IndexTag: string; +begin + if Active then + begin + GetIndexParams(Name, FALSE, AnIndexName, IndexTag); + CheckBrowseMode; + Check(Engine, Engine.DeleteIndex(DBHandle, Handle, '', '', AnIndexName, IndexTag, 0)); + end + else + begin + GetIndexParams(Name, FALSE, AnIndexName, IndexTag); + SetDBFlag(dbfTable, TRUE); + try + Check(Engine, Engine.DeleteIndex(DBHandle, nil, FTableName, '', + AnIndexName, IndexTag, 0)); + finally + SetDBFlag(dbfTable, FALSE); + end; + end; + FIndexDefs.Updated := FALSE; +end; + +function TPSQLTable.GetIndexFieldNames: string; +begin + if FFieldsIndex then Result := FIndexName else Result := ''; +end; + +function TPSQLTable.GetIndexName: string; +begin + if FFieldsIndex then Result := '' else Result := FIndexName; +end; + +procedure TPSQLTable.GetIndexNames(List: TStrings); +begin + IndexDefs.Update; + IndexDefs.GetItemNames(List); +end; + +procedure TPSQLTable.GetIndexParams(const IndexName: string; + FieldsIndex: Boolean; var IndexedName, IndexTag: string); +var + IndexStr: TIndexName; +begin + IndexStr := ''; + if IndexName <> '' then + begin + IndexDefs.Update; + IndexStr := IndexName; + if FieldsIndex then + IndexStr := IndexDefs.FindIndexForFields(IndexName).Name; + end; + IndexedName := IndexStr; + IndexTag := ''; +end; + +procedure TPSQLTable.SetIndexDefs(Value: TIndexDefs); +begin + IndexDefs.Assign(Value); +end; + +procedure TPSQLTable.SetIndex(const Value: string; FieldsIndex: Boolean); +var + AnIndexName, IndexTag: string; +begin + if Active then CheckBrowseMode; + if (FIndexName <> Value) or (FFieldsIndex <> FieldsIndex) then + begin + if Active then + begin + GetIndexParams(Value, FieldsIndex, AnIndexName, IndexTag); + SwitchToIndex(AnIndexName, IndexTag); + CheckMasterRange; + end; + FIndexName := Value; + FFieldsIndex := FieldsIndex; + if Active then Resync([]); + end; +end; + +procedure TPSQLTable.SetIndexFieldNames(const Value: string); +begin + SetIndex(Value, Value <> ''); +end; + +procedure TPSQLTable.SetIndexName(const Value: string); +begin + SetIndex(Value, FALSE); +end; + +procedure TPSQLTable.UpdateIndexDefs; +var + Opts: TIndexOptions; + IdxName, Src, Flds, DescFlds: string; + + procedure UpdateFromCursor; + var + I: Integer; + Cursor: HDBICur; + CursorProps: CurProps; + IndexDescs: TIDXDescList; + begin + if Handle = nil then + Cursor := GetHandle('', '') else + Cursor := Handle; + try + Engine.GetCursorProps(Cursor, CursorProps); + if CursorProps.iIndexes > 0 then + begin + SetLength(IndexDescs, CursorProps.iIndexes); + Engine.GetIndexDescs(Cursor, IndexDescs); + for I := 0 to CursorProps.iIndexes - 1 do + begin + DecodeIndexDesc(IndexDescs[I], Src, IdxName, Flds, DescFlds, Opts); + with IndexDefs.AddIndexDef do + begin + Name := IdxName; + Fields := Flds; + DescFields := DescFlds; + Options := Opts; + if Src <> '' then Source := Src; + end; + end; + end; + finally + if (Cursor <> nil) and (Cursor <> Handle) then Engine.CloseCursor(Cursor); + end; + end; + + procedure UpdateFromIndexList; + var + FCursor: HDBICur; + IndexDesc: IDXDesc; + begin + while not CheckOpen(Engine.OpenIndexList(DBHandle, FTableName, '', FCursor)) do {Retry}; + try + while Engine.GetNextRecord(FCursor, dbiNoLock, @IndexDesc, nil) = 0 do + if IndexDesc.bMaintained then + begin + DecodeIndexDesc(IndexDesc, Src, IdxName, Flds, DescFlds, Opts); + with IndexDefs.AddIndexDef do + begin + Name := IdxName; + Fields := Flds; + DescFields := DescFlds; + Options := Opts; + end; + Finalize(IndexDesc); + end; + finally + Engine.CloseCursor(FCursor); + end; + end; + +begin + Inc(FDatabase.FRefCount); + SetDBFlag(dbfIndexList, TRUE); + try + FieldDefs.Update; + IndexDefs.Clear; + if IsCursorOpen then + UpdateFromCursor else + UpdateFromIndexList; + finally + SetDBFlag(dbfIndexList, FALSE); + end; +end; + +function TPSQLTable.GetExists: Boolean; +begin + Result := Active; + if Result or (TableName = '') or not Assigned(Database) then Exit; + SetDBFlag(dbfTable, TRUE); + try + Database.SelectString('SELECT ' + QuotedStr(TableName) + ' :: regclass', Result) + finally + SetDBFlag(dbfTable, FALSE); + end; +end; + +function TPSQLTable.FindKey(const KeyValues: array of const): Boolean; +begin + CheckBrowseMode; + SetKeyFields(kiLookup, KeyValues); + Result := GotoKey; +end; + +procedure TPSQLTable.FindNearest(const KeyValues: array of const); +begin + CheckBrowseMode; + SetKeyFields(kiLookup, KeyValues); + GotoNearest; +end; + +{$HINTS OFF} +function TPSQLTable.GotoKey: Boolean; +var + KeyBuffer: PKeyBuffer; + IndexBuffer, RecBuffer: PAnsiDACChar; + UseKey: Boolean; +begin + CheckBrowseMode; + DoBeforeScroll; + CursorPosChanged; + KeyBuffer := GetKeyBuffer(kiLookup); + IndexBuffer := AllocMem(KeySize); + try + RecBuffer := PAnsiDACChar(KeyBuffer) + SizeOf(TKeyBuffer); + UseKey := Engine.ExtractKey(Handle, RecBuffer, IndexBuffer) = 0; + if UseKey then RecBuffer := IndexBuffer; + Result := Engine.GetRecordForKey(Handle, UseKey, KeyBuffer^.FieldCount, 0, RecBuffer, nil,True) = 0; + if Result then Resync([rmExact, rmCenter]); + if Result then DoAfterScroll; + finally + FreeMem(IndexBuffer, KeySize); + end; +end; + +procedure TPSQLTable.GotoNearest; +var + SearchCond: DBISearchCond; + KeyBuffer: PKeyBuffer; + IndexBuffer, RecBuffer: PAnsiDACChar; + UseKey: Boolean; +begin + CheckBrowseMode; + CursorPosChanged; + KeyBuffer := GetKeyBuffer(kiLookup); + if KeyBuffer^.Exclusive then + SearchCond := keySEARCHGT else + SearchCond := keySEARCHGEQ; + IndexBuffer := AllocMem(KeySize); + try + RecBuffer := PAnsiDACChar(KeyBuffer) + SizeOf(TKeyBuffer); + UseKey := Engine.ExtractKey(Handle,RecBuffer,IndexBuffer) = 0; + if UseKey then RecBuffer := IndexBuffer; + + if Engine.GetRecordForKey(Handle, {SearchCond,} UseKey, KeyBuffer^.FieldCount, 0,RecBuffer, nil, False) = 0 + then Resync([rmCenter]); + finally + FreeMem(IndexBuffer, KeySize); + end; +end; +{$HINTS ON} + +procedure TPSQLTable.SetKey; +begin + SetKeyBuffer(kiLookup, TRUE); +end; + +procedure TPSQLTable.EditKey; +begin + SetKeyBuffer(kiLookup, FALSE); +end; + +procedure TPSQLTable.ApplyRange; +begin + CheckBrowseMode; + if SetCursorRange then First; +end; + +procedure TPSQLTable.CancelRange; +begin + CheckBrowseMode; + UpdateCursorPos; + if ResetCursorRange then Resync([]); +end; + +procedure TPSQLTable.SetRange(const StartValues, EndValues: array of const); +begin + CheckBrowseMode; + SetKeyFields(kiRangeStart, StartValues); + SetKeyFields(kiRangeEnd, EndValues); + ApplyRange; +end; + +procedure TPSQLTable.SetRangeEnd; +begin + SetKeyBuffer(kiRangeEnd, TRUE); +end; + +procedure TPSQLTable.SetRangeStart; +begin + SetKeyBuffer(kiRangeStart, TRUE); +end; + +procedure TPSQLTable.EditRangeEnd; +begin + SetKeyBuffer(kiRangeEnd, FALSE); +end; + +procedure TPSQLTable.EditRangeStart; +begin + SetKeyBuffer(kiRangeStart, FALSE); +end; + +procedure TPSQLTable.UpdateRange; +begin + SetLinkRanges(FMasterLink.Fields); +end; + +function TPSQLTable.GetBatchModify: Boolean; +var + Len : integer; +begin + if Assigned(FHandle) then + Engine.GetEngProp(hDBIObj(FHandle), curAUTOREFETCH, @Result, SizeOf(Result), Len) + else + Result := False; +end; + +procedure TPSQLTable.SetBatchModify(const Value : Boolean); +begin + if FHandle = nil then Exit; + if Value then + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),curAUTOREFETCH,LongInt(TRUE))) else + begin + Check(Engine, Engine.SetEngProp(hDbiObj(FHandle),curAUTOREFETCH,LongInt(FALSE))); + Refresh; + end; +end; + +procedure TPSQLTable.GotoCurrent(Table: TPSQLTable); +begin + CheckBrowseMode; + Table.CheckBrowseMode; + if (AnsiCompareText(FDatabase.DatabaseName, Table.Database.DatabaseName) <> 0) or + (AnsiCompareText(TableName, Table.TableName) <> 0) then DatabaseError(STableMismatch, Self); + Table.UpdateCursorPos; + DoBeforeScroll; + Resync([rmExact, rmCenter]); + DoAfterScroll; +end; + +{$IFNDEF FPC} +procedure TPSQLTable.GetDetailLinkFields(MasterFields, DetailFields: TList{$IFDEF DELPHI_17}{$ENDIF}); +var + i: Integer; + Idx: TIndexDef; +begin + MasterFields.Clear; + DetailFields.Clear; + if (MasterSource <> nil) and (MasterSource.DataSet <> nil) and (Self.MasterFields <> '') then + begin + Idx := nil; + MasterSource.DataSet.GetFieldList(MasterFields, Self.MasterFields); + UpdateIndexDefs; + if IndexName <> '' then + Idx := IndexDefs.Find(IndexName) + else + if IndexFieldNames <> '' then + Idx := IndexDefs.GetIndexForFields(IndexFieldNames, FALSE) + else + for i := 0 to IndexDefs.Count - 1 do + if ixPrimary in IndexDefs[i].Options then + begin + Idx := IndexDefs[i]; + break; + end; + if Idx <> nil then GetFieldList(DetailFields, Idx.Fields); + end; +end; +{$ENDIF} + +procedure TPSQLTable.CheckMasterRange; +begin + if FMasterLink.Active and (FMasterLink.Fields.Count > 0) then + begin + SetLinkRanges(FMasterLink.Fields); + SetCursorRange; + end; +end; + +procedure TPSQLTable.MasterChanged(Sender: TObject); +begin + CheckBrowseMode; + UpdateRange; + ApplyRange; +end; + +procedure TPSQLTable.MasterDisabled(Sender: TObject); +begin + CancelRange; +end; + +function TPSQLTable.GetDataSource: TDataSource; +begin + Result := FMasterLink.DataSource; +end; + +procedure TPSQLTable.SetDataSource(Value: TDataSource); +begin + if IsLinkedTo(Value) then + DatabaseError(SCircularDataLink, Self); + FMasterLink.DataSource := Value; +end; + +function TPSQLTable.GetMasterFields: string; +begin + Result := FMasterLink.FieldNames; +end; + +procedure TPSQLTable.SetMasterFields(const Value: string); +begin + FMasterLink.FieldNames := Value; +end; + +procedure TPSQLTable.DoOnNewRecord; +var + I: Integer; +begin + if FMasterLink.Active and (FMasterLink.Fields.Count > 0) then + for I := 0 to Pred(FMasterLink.Fields.Count) do + IndexFields[I] := TField(FMasterLink.Fields[I]); + Inherited DoOnNewRecord; +end; + +// pg: 01.03.2011 +procedure TPSQLTable.CreateTable; + + function CreateSQLForCreateTable:string; + var j : Integer; + begin + Result := Format('CREATE TABLE %s ( ',[TableName]); + for j := 0 to FieldDefs.Count - 1 do + begin + Result := Result + BDETOPSQLStr(FieldDefs[j]); + if j < FieldDefs.Count - 1 then + Result := Result + ', ' + else + Result := Result + '); '; + end; + end; + +var + i: integer; +begin + CheckInactive; + SetDBFlag(dbfTable, True); + try + Check(Engine,Engine.QExecDirect(DBHandle, CreateSQLForCreateTable, nil, I)); + //indexes + for I := 0 to IndexDefs.Count - 1 do + AddIndex(IndexDefs[I].Name, IndexDefs[I].Fields, IndexDefs[i].Options); + finally + SetDBFlag(dbfTable, False); + end; +end; + +procedure TPSQLTable.EmptyTable; +begin + if Active then + begin + CheckBrowseMode; + Check(Engine, Engine.EmptyTable(DBHandle, Handle, '', '')); + ClearBuffers; + DataEvent(deDataSetChange, 0); + end else + begin + SetDBFlag(dbfTable, TRUE); + try + Check(Engine, Engine.EmptyTable(DBHandle, nil, FTableName, '')); + finally + SetDBFlag(dbfTable, FALSE); + end; + end; +end; + +procedure TPSQLTable.LockTable(LockType: TPSQLLockType; NoWait: boolean); +begin + CheckActive; + if not Database.InTransaction then + DatabaseError('LOCK TABLE can not be used outside the transaction.'); + Check(Engine, Engine.AcqTableLock(Handle, Word(LockType), NoWait)); +end; + +procedure TPSQLTable.EncodeFieldDesc(var FieldDesc: FLDDesc; + const Name: string; DataType: TFieldType; Size, Precision: Integer); +begin + with FieldDesc do + begin + //TAnsiToNative(Engine, Name, szName, SizeOf(szName) - 1); + szName := Name; + iFldType := FldTypeMap[DataType]; + iSubType := FldSubTypeMap[DataType]; + case DataType of + ftString, ftFixedChar, ftBytes, ftVarBytes, ftBlob..ftTypedBinary: + iUnits1 := Size; + ftBCD: + begin + { Default precision is 32, Size = Scale } + if (Precision > 0) and (Precision <= 32) then + iUnits1 := Precision + else + iUnits1 := 32; + iUnits2 := Size; + end; + end; + end; +end; + +procedure TPSQLTable.DataEvent(Event: TDataEvent; Info: TDataEventInfo); +begin + if Event = depropertyChange then + IndexDefs.Updated := FALSE; + Inherited DataEvent(Event, Info); +end; + +function TPSQLTable.GetCanModify: Boolean; +begin + Result := Inherited GetCanModify and not ReadOnly; +end; + +function TPSQLTable.GetTableLevel: Integer; +begin + if Handle <> nil then + Result := GetIntProp(Engine, Handle, curTABLELEVEL) else + Result := FTableLevel; +end; + +function TPSQLTable.FieldDefsStored: Boolean; +begin + Result := StoreDefs and (FieldDefs.Count > 0); +end; + +function TPSQLTable.IndexDefsStored: Boolean; +begin + Result := StoreDefs and (IndexDefs.Count > 0); +end; + +function TPSQLTable.GetFileName: string; +var + FDb: Boolean; +begin + FDb := SetDBFlag(dbfDatabase, TRUE); + try + Result := Result + TableName; + finally + SetDBFlag(dbfDatabase, FDb); + end; +end; + +function TPSQLTable.GetTableType: TTableType; +begin + Result := ttDefault; +end; + +function TPSQLTable.NativeTableName: PAnsiDACChar; +begin +{$IFNDEF NEXTGEN} + Result := PAnsiChar(AnsiString(FTableName)); +{$ELSE} + Result := PAnsiDACChar(Pointer(FTableName)); +{$ENDIF} +end; + +procedure TPSQLTable.SetExclusive(Value: Boolean); +begin + CheckInactive; + FExclusive := Value; +end; + +procedure TPSQLTable.SetReadOnly(Value: Boolean); +begin + CheckInactive; + FReadOnly := Value; +end; + +procedure TPSQLTable.SetTableName(const Value: TFileName); +begin + if csReading in ComponentState then + FTableName := Value + else + if FTableName <> Value then + begin + CheckInactive; + //changed by pasha_golub 23.12.04 + FTableName := Value; + FNativeTableName[0] := #0; + DataEvent(dePropertyChange, 0); + end; +end; + +function TPSQLTable.GetTableName: TFileName; +begin + Result := FTableName; +end; + +{ TTable.IProviderSupport } +{$IFNDEF FPC} +function TPSQLTable.PSGetDefaultOrder: TIndexDef; + + function GetIdx(IdxType : TIndexOption) : TIndexDef; + var + i: Integer; + L: TList{$IFDEF DELPHI_17}{$ENDIF}; + begin + Result := nil; + L := nil; + for i := 0 to IndexDefs.Count - 1 do + if IdxType in IndexDefs[i].Options then + try + Result := IndexDefs[ i ]; + GetFieldList(L, Result.Fields); + break; + except + Result := nil; + end; + end; + +var + DefIdx: TIndexDef; + L: TList{$IFDEF DELPHI_17}{$ENDIF}; +begin + DefIdx := nil; + L := nil; + IndexDefs.Update; + try + if (IndexName <> '') then + DefIdx := IndexDefs.Find(IndexName) + else + if (IndexFieldNames <> '') then + DefIdx := IndexDefs.FindIndexForFields(IndexFieldNames); + if Assigned(DefIdx) then + GetFieldList(L, DefIdx.Fields); + except + DefIdx := nil; + end; + if not Assigned(DefIdx) then + DefIdx := GetIdx(ixPrimary); + if not Assigned(DefIdx) then + DefIdx := GetIdx(ixUnique); + if Assigned(DefIdx) then + begin + Result := TIndexDef.Create(nil); + Result.Assign(DefIdx); + end + else + Result := nil; +end; + +function TPSQLTable.PSGetIndexDefs(IndexTypes : TIndexOptions): TIndexDefs; +begin + Result := GetIndexDefs(IndexDefs, IndexTypes); +end; + +function TPSQLTable.PSGetTableName: string; +begin + Result := TableName; +end; + +procedure TPSQLTable.PSSetParams(AParams: TParams); + + procedure AssignFields; + var + I: Integer; + begin + for I := 0 to AParams.Count - 1 do + if (AParams[ I ].Name <> '') then + FieldByName(AParams[ I ].Name).Value := AParams[ I ].Value + else + IndexFields[ I ].Value := AParams[ I ].Value; + end; + +begin + if (AParams.Count > 0) then + begin + Open; + SetRangeStart; + AssignFields; + SetRangeEnd; + AssignFields; + ApplyRange; + end + else + if Active then + CancelRange; + PSReset; +end; + +procedure TPSQLTable.PSSetCommandText(const CommandText : string); +begin + if CommandText <> '' then + TableName := CommandText; +end; + +function TPSQLTable.PSGetKeyFields: string; +var + i, Pos: Integer; + IndexFound: Boolean; +begin + Result := inherited PSGetKeyFields; + if Result = '' then + begin + if not Exists then Exit; + IndexFound := FALSE; + IndexDefs.Update; + for i := 0 to IndexDefs.Count - 1 do + if ixUnique in IndexDefs[I].Options then + begin + Result := IndexDefs[ I ].Fields; + IndexFound := (FieldCount = 0); + if not IndexFound then + begin + Pos := 1; + while (Pos <= Length(Result)) do + begin + IndexFound := (FindField(ExtractFieldName(Result, Pos)) <> nil); + if not IndexFound then + Break; + end; + end; + if IndexFound then Break; + end; + if not IndexFound then Result := ''; + end; +end; +{$ENDIF} + +/////////////////////////////////////////////////////////////////////////////// +// TPSQLBlobStream // +/////////////////////////////////////////////////////////////////////////////// +constructor TPSQLBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode); +var + OpenMode: DbiOpenMode; +begin + inherited Create; + FMode := Mode; + FField := Field; + FDataSet := FField.DataSet as TPSQLDataSet; + FFieldNo := FField.FieldNo; + + if not FDataSet.GetActiveRecBuf(FBuffer) then Exit; + + if FDataSet.State = dsFilter then + DatabaseErrorFmt(SNoFieldAccess, [FField.DisplayName], FDataSet); + + if not FField.Modified then + begin + if Mode = bmRead then + begin + FCached := FDataSet.FCacheBlobs and (FBuffer = TRecordBuffer(FDataSet.ActiveBuffer)) and + (FField.IsNull or (FDataSet.GetBlobData(FField, FBuffer) <> {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF})); + OpenMode := dbiReadOnly; + end + else + begin //bmWrite + FDataSet.SetBlobData(FField, FBuffer, {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}); + if FField.ReadOnly then DatabaseErrorFmt(SFieldReadOnly, [FField.DisplayName], FDataSet); + if not (FDataSet.State in [dsEdit, dsInsert]) then DatabaseError(SNotEditing, FDataSet); + OpenMode := dbiReadWrite; + end; + + if not FCached then + begin + if Mode = bmRead then + begin + if FDataSet.State = dsBrowse then + begin + FDataSet.GetCurrentRecord(FDataSet.ActiveBuffer); + end + else if (FDataSet.State = dsEdit) or (FDataSet.State = dsInsert) then + begin + TNativeDataSet(FDataSet.FHandle).PreventRememberBuffer := true; //we just need to read the record without storing in recordbuffer + FDataSet.GetCurrentRecord(FDataSet.ActiveBuffer); + TNativeDataSet(FDataSet.FHandle).PreventRememberBuffer := false; + end; + end; + + Check(Engine, Engine.OpenBlob(FDataSet.Handle, FBuffer, FFieldNo, OpenMode)); + end; + + end; + + FOpened := TRUE; + + if Mode = bmWrite then Truncate; +end; + +destructor TPSQLBlobStream.Destroy; +begin + if FOpened then + begin + if FModified then FField.Modified := TRUE; + if not FField.Modified and not FCached then Engine.FreeBlob(FDataSet.Handle, FBuffer, FFieldNo); + Engine.CloseBlob(FDataset.Handle, FFieldNo); //17.08.2009 + end; + if FModified then + try + FDataSet.DataEvent(deFieldChange, TDataEventInfo(FField)); + except + {$IFNDEF FPC} + Self.FDataSet.InternalHandleException(); + {$ENDIF} + end; + inherited; +end; + +function TPSQLBlobStream.Engine : TPSQLEngine; +begin + Result := FDataSet.Engine; +end; + +function TPSQLBlobStream.PositionDataset: Boolean; +begin + Result := True; +end; + +{$IFDEF DELPHI_17} +function TPSQLBlobStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint; +begin + Result := Read(Buffer[0], Count); +end; +{$ENDIF DELPHI_17} + +function TPSQLBlobStream.Read(var Buffer; Count: Longint): Longint; +var + Status: DBIResult; + Res: integer; +begin + Result := 0; + //P := @Buffer; + if FOpened then + begin + if FCached then + begin + if Count > Size - FPosition then + Result := Size - FPosition else + Result := Count; + if Result > 0 then + begin + Move(PAnsiDACChar(FDataSet.GetBlobData(FField, FBuffer))[FPosition], Buffer, Res); + Result := Res; //compiler wants implicit type casting for NEXTGEN + Inc(FPosition, Result); + end; + end else + begin + Status := Engine.GetBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition, Count, @Buffer, Res); + Result := Res; //compiler wants implicit type casting for NEXTGEN + case Status of + DBIERR_NONE, DBIERR_ENDOFBLOB: + begin + {if FField.Transliterate then + TNativeToAnsiBuf(Engine, @Buffer, @Buffer, Result);} + if FDataset.FCacheBlobs and (FBuffer = TRecordBuffer(FDataSet.ActiveBuffer)) and + (FMode = bmRead) and not FField.Modified and (FPosition = FCacheSize) then + begin + FCacheSize := FPosition + Result; + SetLength(FBlobData, FCacheSize); + Move(Buffer, PAnsiDACChar(FBlobData)[FPosition], Result); + if FCacheSize = Size then + begin + FDataSet.SetBlobData(FField, FBuffer, FBlobData); + FBlobData := {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}; + FCached := TRUE; + Engine.FreeBlob(FDataSet.Handle, FBuffer, FFieldNo); + end; + end; + Inc(FPosition, Result); + end; + DBIERR_INVALIDBLOBOFFSET: + {Nothing}; + else + TDbiError(Engine, Status); + end; + end; + end; +end; + +function TPSQLBlobStream.Write(const Buffer; Count: Longint): Longint; +var + Temp, P: Pointer; +begin + Result := 0; + P := @Buffer; + FField.Transliterate := false; + if FOpened then + begin + if FField.Transliterate then + begin + GetMem(Temp, Count+1); + try + //TAnsiToNativeBuf(Engine, @Buffer, Temp, Count); + Check(Engine, Engine.PutBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition, Count, @Buffer)); + finally + FreeMem(Temp, Count+1); + end; + end else + Check(Engine, Engine.PutBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition, Count, P)); + Inc(FPosition, Count); + Result := Count; + FModified := TRUE; + FDataSet.SetBlobData(FField, FBuffer, {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}); + end; +end; + + +function TPSQLBlobStream.Seek(Offset: Longint; Origin: Word): Longint; +begin + case Origin of + 0: FPosition := Offset; + 1: Inc(FPosition, Offset); + 2: FPosition := GetBlobSize + Offset; + end; + Result := FPosition; +end; + +procedure TPSQLBlobStream.Truncate; +begin + if FOpened then + begin + Check(Engine, Engine.TruncateBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition)); + FModified := TRUE; + FDataSet.SetBlobData(FField, FBuffer, {$IFDEF DELPHI_12}nil{$ELSE}''{$ENDIF}); + end; +end; + +function TPSQLBlobStream.GetBlobSize: integer; +begin + Result := 0; + if FOpened then + if FCached then + Result := Length(FDataSet.GetBlobData(FField, FBuffer)) else + Check(Engine, Engine.GetBlobSize(FDataSet.Handle, FBuffer, FFieldNo, Result)); +end; + +{ TPSQLQueryDataLink } +constructor TPSQLQueryDataLink.Create(AQuery : TPSQLQuery); +begin + Inherited Create; + FQuery := AQuery; +end; + +procedure TPSQLQueryDataLink.ActiveChanged; +begin + if FQuery.Active then FQuery.RefreshParams; +end; + +function TPSQLQueryDataLink.GetDetailDataSet: TDataSet; +begin + Result := FQuery; +end; + +procedure TPSQLQueryDataLink.RecordChanged(Field : TField); +begin + if (Field = nil)and FQuery.Active then FQuery.RefreshParams; +end; + +procedure TPSQLQueryDataLink.CheckBrowseMode; +begin + if FQuery.Active then FQuery.CheckBrowseMode; +end; + +var + SaveInitProc: Pointer; + +procedure InitDBTables; +begin + if (SaveInitProc <> nil) then + TProcedure(SaveInitProc); +end; + + +procedure TPSQLTable.SetShowSystemTables(const Value: boolean); +begin + if FShowSystemTables <> Value then + begin + FShowSystemTables := Value; + IF not Value and + ((pos('pg_',FTableName) = 1) or + (pos('information_schema',FTableName)=1)) then + TableName := ''; + end; +end; + + + +{ TPSQLStoredProc } +const + SEmptyprocedureName = 'procedure name is empty'; + SDatabaseProperty = '(%s) property Database is not set!'; + SCantCreateWriteBLOB = 'Can''t create BLOB stream with write permissions on read-only result set!'; + +procedure TPSQLStoredProc.CloseCursor; +var + r : DbiResult; +begin + inherited; + + if FHandle <> nil then + begin + r := Engine.CloseCursor(FHandle); + FHandle := nil; + if not (csDestroying in ComponentState) then + Check(Engine, r); + end; +end; + +constructor TPSQLStoredProc.Create(AOwner: TComponent); +begin + inherited; + FParams := TPSQLParams.Create(Self); + FNeedRefreshParams := false; +end; + +function TPSQLStoredProc.CreateBlobStream(Field: TField; + Mode: TBlobStreamMode): TStream; +begin + if Mode = bmRead then + Result := TPSQLBlobStream.Create(Field as TBlobField, Mode) + else + raise EPSQLDatabaseError.CreateFmt(SCantCreateWriteBLOB,[]); +end; + +function TPSQLStoredProc.CreateCursor(IsExecProc : boolean): HDBICur; +var + PCursor: phDBICur; + AffectedRows: integer; +begin + Result := nil; + + if Database=nil then + DatabaseError(Format(SDatabaseProperty, [Self.Name]),Self); + + if Length(FProcName) = 0 then + begin + DatabaseError(SEmptyprocedureName, Self); + exit; + end; + + RefreshParams; + + try + SetBoolProp(Engine, hDBIStmt(FHandle), stmtLIVENESS, false);//procedure results alway readonly + while not CheckOpen(Engine.QPrepareProc(GetDBHandle, PChar(FProcName), FParams, hDBIStmt(FHandle))) do + {Retry}; + except + if FHandle <> nil then + begin + Engine.CloseCursor(FHandle); + FHandle := nil; + end; + raise; + end; + + if IsExecProc then + PCursor := nil + else + PCursor := @Result; + + Check(Engine, Engine.QSetProcParams(hDBIStmt(FHandle), FParams)); + + Check(Engine, Engine.QExec(hDBIStmt(FHandle), PCursor, AffectedRows)); +end; + +function TPSQLStoredProc.CreateHandle: HDBICur; +begin + Result := HDBICur(CreateCursor(false)); +end; + +procedure TPSQLStoredProc.DataEvent(Event: TDataEvent; Info: TDataEventInfo); +var + F: TField; + i: integer; +begin + inherited DataEvent(Event, Info); + if (Event = deUpdateState) and (State = dsBrowse) then + for i := 0 to FParams.Count - 1 do + if FParams[i].ParamType in [ptOutput, ptInputOutput] then + begin + F := FieldByName(FParams[i].Name); + if Assigned(F) then + Params[i].Value := F.Value; + end; +end; + +function TPSQLStoredProc.DescriptionsAvailable: Boolean; +begin + Result := True; +end; + +destructor TPSQLStoredProc.Destroy; +begin + FParams.Free; + inherited; +end; + +function TPSQLStoredProc.Engine: TPSQLEngine; +begin + Result := FDataBase.Engine; +end; + +procedure TPSQLStoredProc.ExecProc; +begin + CheckInActive; + + SetDBFlag(dbfExecSQL, TRUE); + try + CreateCursor(true); + finally + SetDBFlag(dbfExecSQL, FALSE); + + if FHandle <> nil then + begin + Check(Engine, Engine.CloseCursor(hDBICur(FHandle))); + FHandle := nil; + end; + end; +end; + +function TPSQLStoredProc.GetCanModify: Boolean; +begin + Result := false; //mi:2006-10-30 we never able modify resultset of stored procedure +end; + +function TPSQLStoredProc.GetParamsCount: integer; +begin + if [csDesigning, csReading] * ComponentState = [] then + RefreshParams; + Result := FParams.Count; +end; + +function TPSQLStoredProc.GetParamsList: TPSQLParams; +begin + if [csDesigning, csReading] * ComponentState = [] then + RefreshParams; + Result := FParams; +end; + +function TPSQLStoredProc.ParamByName(const Value: string): TPSQLParam; +begin + Result := FParams.ParamByName(Value); +end; + +{$IFNDEF FPC} +procedure TPSQLStoredProc.PSExecute; +begin + inherited; + +end; + +function TPSQLStoredProc.PSGetParams: TParams; +begin + Result := FParams; +end; + +function TPSQLStoredProc.PSGetTableName: string; +begin + Result := FProcName; +end; + +procedure TPSQLStoredProc.PSSetCommandText(const CommandText: string); +begin + inherited; + +end; + +procedure TPSQLStoredProc.PSSetParams(AParams: TParams); +begin + inherited; + +end; +{$ENDIF} + +procedure TPSQLStoredProc.RefreshParams; +var + Desc: ^SPParamDesc; + ParamName: string; + ParamDataType: TFieldType; + List : TList{$IFDEF NEXTGEN}{$ENDIF}; + i:integer; +begin + if not FNeedRefreshParams or not FDatabase.Connected then Exit; + List := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + try + FParams.Clear; + Check(Engine, Engine.OpenStoredProcParams(DBHandle, StoredProcName, FOverload, List)); + for i:=0 to List.Count-1 do + begin + Desc := List[i]; + with Desc^ do + begin + ParamName := szName; + if (TParamType(eParamType) = ptResult) and (ParamName = '') then + ParamName := SResultName; + if uFldType < MAXLOGFLDTYPES then ParamDataType := DataTypeMap[uFldType] + else ParamDataType := ftUnknown; + case uFldtype of + fldFloat: + if uSubType = fldstMONEY then ParamDataType := ftCurrency; + fldBlob: + if (uSubType >= fldstMEMO) and (uSubType <= fldstBFILE) then + ParamDataType := BlobTypeMap[uSubType]; + end; + with TParam(FParams.Add) do + begin + ParamType := TParamType(eParamType); + DataType := ParamDataType; + Name := string(ParamName); + end; + end; + end; + finally + for i:=0 to List.Count-1 do + begin + Desc := List[i]; + Dispose(Desc); + end; + List.Free; + FNeedRefreshParams := false; + end; +end; + +procedure TPSQLStoredProc.SetNeedRefreshParams; +begin + FNeedRefreshParams := true; +end; + +procedure TPSQLStoredProc.SetOverload(const Value: cardinal); +begin + if ([csReading, csLoading] * ComponentState = []) then + SetNeedRefreshParams(); + FOverload := Value; + RefreshParams; + DataEvent(dePropertyChange, 0); +end; + +procedure TPSQLStoredProc.SetParamsList(const Value: TPSQLParams); +begin + FParams.AssignValues(Value); +end; + +procedure TPSQLStoredProc.SetProcedureName(const Value: string); +begin + if ([csReading, csLoading] * ComponentState = []) then + SetNeedRefreshParams(); + FProcName := Value; + RefreshParams; + DataEvent(dePropertyChange, 0); +end; + +procedure TPSQLStoredProc.SetProcName(const Value: string); +begin + if ([csReading, csLoading] * ComponentState = []) then + SetNeedRefreshParams(); + FProcName := Value; + RefreshParams; + DataEvent(dePropertyChange, 0); +end; + +function TPSQLTable.GetOffset: Integer; +begin + Result := FOffset; +end; + +procedure TPSQLTable.SetOffset(const Value: Integer); +begin + FOffset := Value; +end; + +procedure TPSQLTable.SetDummyInt(const Value: cardinal); +begin +//dummy method for published +end; + +procedure TPSQLTable.SetDummyStr(const Value: string); +begin +//dummy method for published +end; + +function TPSQLTable.GetTableSpace: string; +begin + if (FTableSpace = '') and Database.Connected then + Result := Database.Tablespace + else + Result := FTablespace; +end; + +procedure TPSQLTable.SetOptions(const Value: TPSQLDatasetOptions); +begin + if Value = FOptions then Exit; + inherited; + if Active then + begin + Close; + Open; + end; +end; + +{ TPSQLParams } +constructor TPSQLParams.Create(Owner: TPersistent); +begin + FOwner := Owner; + inherited Create(TPSQLParam); +end; + +procedure TPSQLParams.AssignValues(Value: TParams); +begin + inherited; +end; + +function TPSQLParams.GetOwner: TPersistent; +begin + Result := FOwner; +end; + +constructor TPSQLParams.Create; +begin + inherited Create(TPSQLParam); + FOwner := nil; +end; + +function TPSQLParams.CreateParam(FldType: TFieldType; const ParamName: string; + ParamType: TParamType; const DataTypeOID: cardinal = 0; Binary: boolean = False): TPSQLParam; +begin + Result := inherited CreateParam(FldType, ParamName, ParamType) as TPSQLParam; + Result.DataTypeOID := DataTypeOID; + Result.Binary := Binary; +end; + +function TPSQLParams.GetItem(Index: Integer): TPSQLParam; +begin + Result := inherited GetItem(index) as TPSQLParam; +end; + +function TPSQLParams.ParamByName(const Value: string): TPSQLParam; +begin + Result := inherited ParamByName(Value) as TPSQLParam; +end; + +function TPSQLParams.ParseSQL(SQL: string; DoCreate: Boolean): string; +const + {$IFNDEF NEXTGEN} + Literals = ['''', '"', '`'] + {$ELSE} + Literals : array of Char = ['''', '"', '`'] + {$ENDIF}; +var + Value, CurPos, StartPos: PChar; + CurChar: Char; + Literal: Boolean; + EmbeddedLiteral: Boolean; + Name: string; + + function NameDelimiter: Boolean; + begin + {$IFNDEF NEXTGEN} + Result := CharInSet(CurChar, [' ', ',', ';', ')', #13, #10]); + {$ELSE} + Result := CurChar.IsInArray([' ', ',', ';', ')', #13, #10]); + {$ENDIF} + end; + + function IsLiteral: Boolean; + begin + {$IFNDEF NEXTGEN} + Result := CharInSet(CurChar, Literals); + {$ELSE} + Result := CurChar.IsInArray(Literals); + {$ENDIF} + end; + + function StripLiterals(Buffer: PChar): string; + var + Len: integer; + TempBuf: PChar; + + procedure StripChar; + begin + {$IFNDEF NEXTGEN} + if CharInSet(TempBuf^, Literals) then + {$ELSE} + if TempBuf^.IsInArray(Literals) then + {$ENDIF} + begin + StrMove(TempBuf, TempBuf + 1, Len - 1); + {$IFNDEF NEXTGEN} + if CharInSet((TempBuf + (Len-2))^, Literals) then + {$ELSE} + if (TempBuf + (Len-2))^.IsInArray(Literals) then + {$ENDIF} + (TempBuf + Len-2)^ := #0; + end; + end; + + begin + Len := StrLen(Buffer); + TempBuf := AllocMem((Len + 1) * SizeOf(Char)); + try + StrCopy(TempBuf, Buffer); + StripChar; + Result := TempBuf; + finally + FreeMem(TempBuf, (Len + 1) * SizeOf(Char)); + end; + end; + +begin + + Result := SQL; + Value := PChar(Result); + if DoCreate then Clear; + CurPos := Value; + Literal := False; + EmbeddedLiteral := False; + repeat + CurChar := CurPos^; + if (CurChar = ':') and not Literal and + {$IFNDEF NEXTGEN} + not CharInSet((CurPos + 1)^, [':', '=', ' ', ',', ';', #9, #13, #10]) then + {$ELSE} + not (CurPos + 1)^.IsInArray([':', '=', ' ', ',', ';', #9, #13, #10]) then + {$ENDIF} + begin + StartPos := CurPos; + while (CurChar <> #0) and (Literal or not NameDelimiter) do + begin + Inc(CurPos); + CurChar := CurPos^; + if IsLiteral then + begin + Literal := Literal xor True; + if CurPos = StartPos + 1 then EmbeddedLiteral := True; + end; + end; + CurPos^ := #0; + if EmbeddedLiteral then + begin + Name := StripLiterals(StartPos + 1); + EmbeddedLiteral := False; + end + else Name := string(StartPos + 1); + if DoCreate and not Assigned(FindParam(Name)) then + TParam(Add).Name := Name; + CurPos^ := CurChar; + StartPos^ := '?'; + Inc(StartPos); + StrMove(StartPos, CurPos, StrLen(CurPos) + 1); + CurPos := StartPos; + end + else if (CurChar = ':') and not Literal and + {$IFNDEF NEXTGEN} + CharInSet((CurPos + 1)^, [':', '=', ' ', ',', ';', #9, #13, #10]) then + {$ELSE} + ((CurPos + 1)^.IsInArray([':', '=', ' ', ',', ';', #9, #13, #10])) then + {$ENDIF} + StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1) + else if IsLiteral then Literal := Literal xor True; + Inc(CurPos); + until CurChar = #0; +end; + +procedure TPSQLParams.SetItem(Index: Integer; const Value: TPSQLParam); +begin + inherited SetItem(Index, TCollectionItem(Value)); +end; + +{ TPSQLParam } + +function TPSQLParam.IsEqual(Value: TParam): Boolean; +begin + Result := inherited IsEqual(Value); + if Value is TPSQLParam then + Result := Result + and (FDataTypeOid = TPSQLParam(Value).DataTypeOid) + and (FBinary = TPSQLParam(Value).Binary); +end; + +procedure TPSQLParam.SetDataTypeOID(const Value: cardinal); +begin + FDataTypeOID := Value; +end; + +{$IFDEF DELPHI_12} +{ TPSQLLookupList } + +procedure TPSQLLookupList.Add(const AKey, AValue: Variant); +var + ListEntry: PLookupListEntry; +begin + New(ListEntry); + ListEntry.Key := AKey; + ListEntry.Value := AValue; + FList.Add(ListEntry); +end; + +procedure TPSQLLookupList.Clear; +var + I: Integer; +begin + for I := 0 to FList.Count - 1 do + Dispose(PLookupListEntry(FList.Items[I])); + FList.Clear; +end; + +constructor TPSQLLookupList.Create; +begin + inherited; + FList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; +end; + +destructor TPSQLLookupList.Destroy; +begin + if FList <> nil then Clear; + FList.Free; +end; + +function TPSQLLookupList.ValueOfKey(const AKey: Variant): Variant; +var + I: Integer; +begin + Result := Null; + if VarIsNull(AKey) then Exit; + if VarIsArray(AKey) then + begin + for I := 0 to FList.Count - 1 do + if IsSameVarArrays(PLookupListEntry(FList.Items[I]).Key, AKey) then + begin + Result := PLookupListEntry(FList.Items[I]).Value; + Exit; + end; + end + else + for I := 0 to FList.Count - 1 do + if PLookupListEntry(FList.Items[I]).Key = AKey then + begin + Result := PLookupListEntry(FList.Items[I]).Value; + Break; + end; +end; + +{ TPSQLFieldDef } + +procedure TPSQLFieldDef.SetNativeDataType(const Value: cardinal); +begin + FNativeDataType := Value; + Changed(False); +end; + +{ TPSQLFieldDefs } + +function TPSQLFieldDefs.GetFieldDefClass: TFieldDefClass; +begin + Result := TPSQLFieldDef; +end; +{$ENDIF DELPHI_12} + +initialization + + if not IsLibrary then + begin + SaveInitProc := InitProc; + InitProc := @InitDBTables; + end; + DBList := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + +finalization + + DBList.Free; + DBList := nil; + + if not IsLibrary then + InitProc := SaveInitProc; + +end. diff --git a/PSQLDirectQuery.pas b/Source/PSQLDirectQuery.pas similarity index 96% rename from PSQLDirectQuery.pas rename to Source/PSQLDirectQuery.pas index eb5f622..3f4f6d6 100644 --- a/PSQLDirectQuery.pas +++ b/Source/PSQLDirectQuery.pas @@ -1,381 +1,381 @@ -{$I pSQLDAC.inc} - -unit PSQLDirectQuery; - -{SVN revision: $Id$} - -interface - -uses - SysUtils, Classes, PSQLDbTables, PSQLTypes; - -type - EPSQLDirectQueryException = class(Exception); - - TPSQLCustomDirectQuery = class(TComponent) - private - FDatabase: TPSQLDatabase; - FSQL : TStrings; - FStatement : PPGresult; - FAbout : TPSQLDACAbout; - FRecNo: integer; - FEOF: boolean; - FBOF: boolean; - FParams: TPSQLParams; - FBinaryDataFormat: boolean; - - procedure FreeHandle(); - function GetActive(): boolean; - procedure SetActive(const Value: boolean); - procedure CheckOpen();//raise an exception if dataset is not active - function GetRecNo: integer; - procedure SetRecNo(const Value: integer); - function GetRecordCount: integer; - function GetIsEmpty: boolean; - procedure SetSQL(const Value: TStrings); - function GetFieldValue(aIndex: integer): string; - function GetFieldsCount : integer; - function GetFieldName(aIndex: integer): string; - procedure SetParamsList(const Value: TPSQLParams); - function GetStoreActive: Boolean; - protected - procedure SetDatabase(Value : TPSQLDatabase); - function GetDatabase : TPSQLDatabase; - public - constructor Create(aOwner: TComponent);override; - destructor Destroy();override; - - procedure Open(); - procedure Close(); - procedure Refresh(); - - procedure First(); - procedure Last(); - procedure Next(); - procedure Prior(); - function MoveBy(aDistance : integer) : integer; - - function FieldValueByFieldName(aFieldName : string) : string; - function FieldIndexByName(aFieldName : string) : integer; - function FieldIsNull(aFieldIndex : integer) : boolean;overload; - function FieldIsNull(aFieldName : string) : boolean;overload; - - property Database : TPSQLDatabase read GetDatabase write SetDatabase; - property Active : boolean read GetActive write SetActive stored GetStoreActive; - property SQL : TStrings read FSQL write SetSQL; - property RecNo : integer read GetRecNo write SetRecNo;//current cursor position - property RecordCount : integer read GetRecordCount; - property BinaryDataFormat: boolean read FBinaryDataFormat write FBinaryDataFormat; //test condition, not use in production - property IsEmpty : boolean read GetIsEmpty; - property Eof : boolean read FEOF; - property Bof : boolean read FBOF; - property FieldsCount : integer read GetFieldsCount; - property FieldValues[aIndex : integer]: string read GetFieldValue; - property FieldNames[aIndex : integer]: string read GetFieldName; - property Params: TPSQLParams read FParams write SetParamsList; - published - property About : TPSQLDACAbout read FAbout; - end; - - TPSQLDirectQuery = class(TPSQLCustomDirectQuery) - published - property Database; - property SQL; - property Params; - end; - -implementation - -uses - {$IFDEF DELPHI_18}{$IFNDEF NEXTGEN}System.AnsiStrings,{$ENDIF}{$ENDIF} - PSQLAccess; - -{ TPSQLCustomDirectQuery } - -procedure TPSQLCustomDirectQuery.CheckOpen(); -begin - if FStatement = nil then - raise EPSQLDirectQueryException.Create(SDataSetClosed); -end; - -procedure TPSQLCustomDirectQuery.Close(); -begin - FreeHandle(); -end; - -constructor TPSQLCustomDirectQuery.Create(aOwner: TComponent); -var I: integer; -begin - inherited Create(AOwner); - FStatement := nil; - FSQL := TStringList.Create(); - FParams := TPSQLParams.Create(Self); - if (csDesigning in ComponentState) and Assigned(AOwner) then - for I := AOwner.ComponentCount - 1 downto 0 do - if AOwner.Components[I] is TPSQLDatabase then - begin - Database := AOwner.Components[I] as TPSQLDatabase; - Break; - end; -end; - -destructor TPSQLCustomDirectQuery.Destroy(); -begin - FParams.Free(); - SetDatabase(nil); - FreeHandle(); - FSQL.Free(); - inherited; -end; - -function TPSQLCustomDirectQuery.FieldIndexByName(aFieldName: string): integer; -var P: PAnsiDACChar; -begin - CheckOpen(); - P := TNativeConnect(FDatabase.Handle).StringToRaw(aFieldName); - try - Result := PQfnumber(FStatement, P); - finally - DACAnsiStrDispose(P); - end; -end; - -function TPSQLCustomDirectQuery.FieldIsNull(aFieldIndex: integer): boolean; -begin - if GetRecordCount() <= 0 then - raise EPSQLDirectQueryException.Create(SDataSetEmpty); - - if aFieldIndex >= GetFieldsCount() then - raise EPSQLDirectQueryException.Create(SFieldIndexError); - Result := PQgetisnull(FStatement, FRecNo, aFieldIndex) = 1; -end; - -function TPSQLCustomDirectQuery.FieldIsNull(aFieldName: string): boolean; -var - i : integer; -begin - i := FieldIndexByName(aFieldName); - if i = -1 then - raise EPSQLDirectQueryException.Create(Format(SFieldNotFound, [aFieldName])); - Result := FieldIsNull(i); -end; - -function TPSQLCustomDirectQuery.FieldValueByFieldName(aFieldName: string): string; -var - i : integer; -begin - i := FieldIndexByName(aFieldName); - if i = -1 then - raise EPSQLDirectQueryException.Create(Format(SFieldNotFound, [aFieldName])); - Result := FieldValues[i]; -end; - -procedure TPSQLCustomDirectQuery.First; -begin - CheckOpen(); - RecNo := 0; -end; - -procedure TPSQLCustomDirectQuery.FreeHandle; -begin - if FStatement <> nil then - begin - PQclear(FStatement); - FStatement := nil; - end; -end; - -function TPSQLCustomDirectQuery.GetActive: boolean; -begin - Result := FStatement <> nil; -end; - -function TPSQLCustomDirectQuery.GetDatabase: TPSQLDatabase; -begin - Result := FDatabase; -end; - -function TPSQLCustomDirectQuery.GetFieldName(aIndex: integer): string; -begin - if aIndex >= GetFieldsCount then - raise EPSQLDirectQueryException.Create(SFieldIndexError); - Result := TNativeConnect(FDatabase.Handle).RawToString(PQfname(FStatement, aIndex)); -end; - -function TPSQLCustomDirectQuery.GetFieldsCount: integer; -begin - CheckOpen(); - Result := PQnfields(FStatement); -end; - -function TPSQLCustomDirectQuery.GetFieldValue(aIndex: integer): string; -begin - Result := ''; - - if GetRecordCount() = 0 then - raise EPSQLDirectQueryException.Create(SDataSetEmpty); - - if aIndex >= GetFieldsCount() then - raise EPSQLDirectQueryException.Create(SFieldIndexError); - - if PQfformat(FStatement, aIndex) = 0 then //text representation - Result := TNativeConnect(FDatabase.Handle).RawToString(PQgetvalue(FStatement, FRecNo, aIndex)) -{$IFDEF DELPHI_15} - else //binary representaion accoridin to typsend & typreceive functions - Result := TNativeConnect(FDatabase.Handle).BinaryToString(PQgetvalue(FStatement, FRecNo, aIndex), PQftype(FStatement, aIndex)) -{$ENDIF} -end; - -function TPSQLCustomDirectQuery.GetIsEmpty: boolean; -begin - Result := GetRecordCount() = 0; -end; - -function TPSQLCustomDirectQuery.GetRecNo: integer; -begin - Result := FRecNo; -end; - -function TPSQLCustomDirectQuery.GetRecordCount: integer; -begin - CheckOpen(); - Result := PQntuples(FStatement); -end; - -function TPSQLCustomDirectQuery.GetStoreActive: Boolean; -begin - Result := Active - and Assigned(FDatabase) - and ( - (ddoStoreConnected in FDatabase.DesignOptions) - or not (csDesigning in ComponentState) - ); -end; - -procedure TPSQLCustomDirectQuery.Last(); -begin - CheckOpen(); - FRecNo := Pred(GetRecordCount()); -end; - -function TPSQLCustomDirectQuery.MoveBy(aDistance: integer): integer; -var - NewRecNo: integer; -begin - CheckOpen(); - - NewRecNo := FRecNo + aDistance; - - if NewRecNo >= GetRecordCount() then - FRecNo := Pred(GetRecordCount()) - else if NewRecNo < 0 then - FRecNo := 0; - - Result := NewRecNo - FRecNo; - - RecNo := NewRecNo; - - if EOF or BOF then - Result := 0; -end; - -procedure TPSQLCustomDirectQuery.Next; -begin - CheckOpen(); - RecNo := RecNo + 1; -end; - -procedure TPSQLCustomDirectQuery.Open(); -var NC: TNativeConnect; -begin - if FStatement <> nil then - Exit;//already opened, use Refresh() if you want to re-read data - - if FDatabase = nil then - raise EPSQLDirectQueryException.Create(SDatabaseNameMissing); - - if Trim(FSQL.Text) = EmptyStr then - raise EPSQLDirectQueryException.Create(SEmptySQLStatement); - - if not FDatabase.Connected then FDatabase.Open; - - NC := TNativeConnect(FDatabase.Handle); - - if (FParams.Count > 0) or FBinaryDataFormat then - FStatement := _PQExecuteParams(NC, FSQL.Text, FParams, ord(FBinaryDataFormat)) - else - FStatement := _PQExecute(NC, FSQL.Text); - if PQresultStatus(FStatement) <> PGRES_TUPLES_OK then - try - NC.CheckResult(FStatement); - FreeHandle(); - except - if FDatabase.Engine().CheckError <> 0 then - raise EPSQLDatabaseError.Create(FDatabase.Engine(), FDatabase.Engine().Status); - FreeHandle(); - end; - RecNo := 0; -end; - -procedure TPSQLCustomDirectQuery.Prior; -begin - CheckOpen(); - RecNo := RecNo - 1; -end; - -procedure TPSQLCustomDirectQuery.Refresh(); -begin - Close(); - Open(); -end; - -procedure TPSQLCustomDirectQuery.SetActive(const Value: boolean); -begin - if Value then - Open() - else - Close(); -end; - -procedure TPSQLCustomDirectQuery.SetDatabase(Value: TPSQLDatabase); -begin - if Active then - Close(); - if Assigned(FDatabase) then - FDatabase.UnregisterDirectQuery(Self); - FDatabase := Value; - if Assigned(FDatabase) then - FDatabase.RegisterDirectQuery(Self); -end; - -procedure TPSQLCustomDirectQuery.SetParamsList(const Value: TPSQLParams); -begin - FParams.AssignValues(Value); -end; - -procedure TPSQLCustomDirectQuery.SetRecNo(const Value: integer); -begin - CheckOpen(); - FEOF := Value >= GetRecordCount(); - FBOF := Value < 0; - if FEOF or FBOF then - Exit; - FRecNo := Value; -end; - -procedure TPSQLCustomDirectQuery.SetSQL(const Value: TStrings); -begin - if FSQL.Text <> Value.Text then - begin - Close(); - FSQL.BeginUpdate(); - try - FSQL.Assign(Value); - finally - FSQL.EndUpdate(); - end; - end; -end; - - -end. +{$I pSQLDAC.inc} + +unit PSQLDirectQuery; + +{SVN revision: $Id$} + +interface + +uses + SysUtils, Classes, PSQLDbTables, PSQLTypes; + +type + EPSQLDirectQueryException = class(Exception); + + TPSQLCustomDirectQuery = class(TComponent) + private + FDatabase: TPSQLDatabase; + FSQL : TStrings; + FStatement : PPGresult; + FAbout : TPSQLDACAbout; + FRecNo: integer; + FEOF: boolean; + FBOF: boolean; + FParams: TPSQLParams; + FBinaryDataFormat: boolean; + + procedure FreeHandle(); + function GetActive(): boolean; + procedure SetActive(const Value: boolean); + procedure CheckOpen();//raise an exception if dataset is not active + function GetRecNo: integer; + procedure SetRecNo(const Value: integer); + function GetRecordCount: integer; + function GetIsEmpty: boolean; + procedure SetSQL(const Value: TStrings); + function GetFieldValue(aIndex: integer): string; + function GetFieldsCount : integer; + function GetFieldName(aIndex: integer): string; + procedure SetParamsList(const Value: TPSQLParams); + function GetStoreActive: Boolean; + protected + procedure SetDatabase(Value : TPSQLDatabase); + function GetDatabase : TPSQLDatabase; + public + constructor Create(aOwner: TComponent);override; + destructor Destroy();override; + + procedure Open(); + procedure Close(); + procedure Refresh(); + + procedure First(); + procedure Last(); + procedure Next(); + procedure Prior(); + function MoveBy(aDistance : integer) : integer; + + function FieldValueByFieldName(aFieldName : string) : string; + function FieldIndexByName(aFieldName : string) : integer; + function FieldIsNull(aFieldIndex : integer) : boolean;overload; + function FieldIsNull(aFieldName : string) : boolean;overload; + + property Database : TPSQLDatabase read GetDatabase write SetDatabase; + property Active : boolean read GetActive write SetActive stored GetStoreActive; + property SQL : TStrings read FSQL write SetSQL; + property RecNo : integer read GetRecNo write SetRecNo;//current cursor position + property RecordCount : integer read GetRecordCount; + property BinaryDataFormat: boolean read FBinaryDataFormat write FBinaryDataFormat; //test condition, not use in production + property IsEmpty : boolean read GetIsEmpty; + property Eof : boolean read FEOF; + property Bof : boolean read FBOF; + property FieldsCount : integer read GetFieldsCount; + property FieldValues[aIndex : integer]: string read GetFieldValue; + property FieldNames[aIndex : integer]: string read GetFieldName; + property Params: TPSQLParams read FParams write SetParamsList; + published + property About : TPSQLDACAbout read FAbout; + end; + + TPSQLDirectQuery = class(TPSQLCustomDirectQuery) + published + property Database; + property SQL; + property Params; + end; + +implementation + +uses + {$IFDEF DELPHI_18}{$IFNDEF NEXTGEN}System.AnsiStrings,{$ENDIF}{$ENDIF} + PSQLAccess; + +{ TPSQLCustomDirectQuery } + +procedure TPSQLCustomDirectQuery.CheckOpen(); +begin + if FStatement = nil then + raise EPSQLDirectQueryException.Create(SDataSetClosed); +end; + +procedure TPSQLCustomDirectQuery.Close(); +begin + FreeHandle(); +end; + +constructor TPSQLCustomDirectQuery.Create(aOwner: TComponent); +var I: integer; +begin + inherited Create(AOwner); + FStatement := nil; + FSQL := TStringList.Create(); + FParams := TPSQLParams.Create(Self); + if (csDesigning in ComponentState) and Assigned(AOwner) then + for I := AOwner.ComponentCount - 1 downto 0 do + if AOwner.Components[I] is TPSQLDatabase then + begin + Database := AOwner.Components[I] as TPSQLDatabase; + Break; + end; +end; + +destructor TPSQLCustomDirectQuery.Destroy(); +begin + FParams.Free(); + SetDatabase(nil); + FreeHandle(); + FSQL.Free(); + inherited; +end; + +function TPSQLCustomDirectQuery.FieldIndexByName(aFieldName: string): integer; +var P: PAnsiDACChar; +begin + CheckOpen(); + P := TNativeConnect(FDatabase.Handle).StringToRaw(aFieldName); + try + Result := PQfnumber(FStatement, P); + finally + DACAnsiStrDispose(P); + end; +end; + +function TPSQLCustomDirectQuery.FieldIsNull(aFieldIndex: integer): boolean; +begin + if GetRecordCount() <= 0 then + raise EPSQLDirectQueryException.Create(SDataSetEmpty); + + if aFieldIndex >= GetFieldsCount() then + raise EPSQLDirectQueryException.Create(SFieldIndexError); + Result := PQgetisnull(FStatement, FRecNo, aFieldIndex) = 1; +end; + +function TPSQLCustomDirectQuery.FieldIsNull(aFieldName: string): boolean; +var + i : integer; +begin + i := FieldIndexByName(aFieldName); + if i = -1 then + raise EPSQLDirectQueryException.Create(Format(SFieldNotFound, [aFieldName])); + Result := FieldIsNull(i); +end; + +function TPSQLCustomDirectQuery.FieldValueByFieldName(aFieldName: string): string; +var + i : integer; +begin + i := FieldIndexByName(aFieldName); + if i = -1 then + raise EPSQLDirectQueryException.Create(Format(SFieldNotFound, [aFieldName])); + Result := FieldValues[i]; +end; + +procedure TPSQLCustomDirectQuery.First; +begin + CheckOpen(); + RecNo := 0; +end; + +procedure TPSQLCustomDirectQuery.FreeHandle; +begin + if FStatement <> nil then + begin + PQclear(FStatement); + FStatement := nil; + end; +end; + +function TPSQLCustomDirectQuery.GetActive: boolean; +begin + Result := FStatement <> nil; +end; + +function TPSQLCustomDirectQuery.GetDatabase: TPSQLDatabase; +begin + Result := FDatabase; +end; + +function TPSQLCustomDirectQuery.GetFieldName(aIndex: integer): string; +begin + if aIndex >= GetFieldsCount then + raise EPSQLDirectQueryException.Create(SFieldIndexError); + Result := TNativeConnect(FDatabase.Handle).RawToString(PQfname(FStatement, aIndex)); +end; + +function TPSQLCustomDirectQuery.GetFieldsCount: integer; +begin + CheckOpen(); + Result := PQnfields(FStatement); +end; + +function TPSQLCustomDirectQuery.GetFieldValue(aIndex: integer): string; +begin + Result := ''; + + if GetRecordCount() = 0 then + raise EPSQLDirectQueryException.Create(SDataSetEmpty); + + if aIndex >= GetFieldsCount() then + raise EPSQLDirectQueryException.Create(SFieldIndexError); + + if PQfformat(FStatement, aIndex) = 0 then //text representation + Result := TNativeConnect(FDatabase.Handle).RawToString(PQgetvalue(FStatement, FRecNo, aIndex)) +{$IFDEF DELPHI_15} + else //binary representaion accoridin to typsend & typreceive functions + Result := TNativeConnect(FDatabase.Handle).BinaryToString(PQgetvalue(FStatement, FRecNo, aIndex), PQftype(FStatement, aIndex)) +{$ENDIF} +end; + +function TPSQLCustomDirectQuery.GetIsEmpty: boolean; +begin + Result := GetRecordCount() = 0; +end; + +function TPSQLCustomDirectQuery.GetRecNo: integer; +begin + Result := FRecNo; +end; + +function TPSQLCustomDirectQuery.GetRecordCount: integer; +begin + CheckOpen(); + Result := PQntuples(FStatement); +end; + +function TPSQLCustomDirectQuery.GetStoreActive: Boolean; +begin + Result := Active + and Assigned(FDatabase) + and ( + (ddoStoreConnected in FDatabase.DesignOptions) + or not (csDesigning in ComponentState) + ); +end; + +procedure TPSQLCustomDirectQuery.Last(); +begin + CheckOpen(); + FRecNo := Pred(GetRecordCount()); +end; + +function TPSQLCustomDirectQuery.MoveBy(aDistance: integer): integer; +var + NewRecNo: integer; +begin + CheckOpen(); + + NewRecNo := FRecNo + aDistance; + + if NewRecNo >= GetRecordCount() then + FRecNo := Pred(GetRecordCount()) + else if NewRecNo < 0 then + FRecNo := 0; + + Result := NewRecNo - FRecNo; + + RecNo := NewRecNo; + + if EOF or BOF then + Result := 0; +end; + +procedure TPSQLCustomDirectQuery.Next; +begin + CheckOpen(); + RecNo := RecNo + 1; +end; + +procedure TPSQLCustomDirectQuery.Open(); +var NC: TNativeConnect; +begin + if FStatement <> nil then + Exit;//already opened, use Refresh() if you want to re-read data + + if FDatabase = nil then + raise EPSQLDirectQueryException.Create(SDatabaseNameMissing); + + if Trim(FSQL.Text) = EmptyStr then + raise EPSQLDirectQueryException.Create(SEmptySQLStatement); + + if not FDatabase.Connected then FDatabase.Open; + + NC := TNativeConnect(FDatabase.Handle); + + if (FParams.Count > 0) or FBinaryDataFormat then + FStatement := _PQExecuteParams(NC, FSQL.Text, FParams, ord(FBinaryDataFormat)) + else + FStatement := _PQExecute(NC, FSQL.Text); + if PQresultStatus(FStatement) <> PGRES_TUPLES_OK then + try + NC.CheckResult(FStatement); + FreeHandle(); + except + if FDatabase.Engine().CheckError <> 0 then + raise EPSQLDatabaseError.Create(FDatabase.Engine(), FDatabase.Engine().Status); + FreeHandle(); + end; + RecNo := 0; +end; + +procedure TPSQLCustomDirectQuery.Prior; +begin + CheckOpen(); + RecNo := RecNo - 1; +end; + +procedure TPSQLCustomDirectQuery.Refresh(); +begin + Close(); + Open(); +end; + +procedure TPSQLCustomDirectQuery.SetActive(const Value: boolean); +begin + if Value then + Open() + else + Close(); +end; + +procedure TPSQLCustomDirectQuery.SetDatabase(Value: TPSQLDatabase); +begin + if Active then + Close(); + if Assigned(FDatabase) then + FDatabase.UnregisterDirectQuery(Self); + FDatabase := Value; + if Assigned(FDatabase) then + FDatabase.RegisterDirectQuery(Self); +end; + +procedure TPSQLCustomDirectQuery.SetParamsList(const Value: TPSQLParams); +begin + FParams.AssignValues(Value); +end; + +procedure TPSQLCustomDirectQuery.SetRecNo(const Value: integer); +begin + CheckOpen(); + FEOF := Value >= GetRecordCount(); + FBOF := Value < 0; + if FEOF or FBOF then + Exit; + FRecNo := Value; +end; + +procedure TPSQLCustomDirectQuery.SetSQL(const Value: TStrings); +begin + if FSQL.Text <> Value.Text then + begin + Close(); + FSQL.BeginUpdate(); + try + FSQL.Assign(Value); + finally + FSQL.EndUpdate(); + end; + end; +end; + + +end. diff --git a/PSQLDump.pas b/Source/PSQLDump.pas similarity index 96% rename from PSQLDump.pas rename to Source/PSQLDump.pas index 903c800..f87b11e 100644 --- a/PSQLDump.pas +++ b/Source/PSQLDump.pas @@ -1,1346 +1,1346 @@ -{$I PSQLDAC.inc} -unit PSQLDump; - -{SVN revision: $Id$} - -interface - -Uses Classes, SysUtils, Db, PSQLTypes, Math, PSQLDbTables; - -type - Tv3_Dump = function (AppName: PAnsiDACChar; LogFileName: PAnsiDACChar; Params : PAnsiDACChar): longint; cdecl; - Tv3_Restore = function (AppName: PAnsiDACChar; LogFileName : PAnsiDACChar; Params : PAnsiDACChar): longint; cdecl; - Tpdmbvm_GetVersionAsInt = function():integer; cdecl; - Tpdmbvm_SetErrorCallBackProc = procedure(ProcAddr : pointer); cdecl; - Tpdmbvm_SetLogCallBackProc = procedure(ProcAddr : pointer); cdecl; - - TpdmvmParams = class - private - FParams : TStringList; - FArr : PAnsiDACChar; - - procedure ClearMem(); - public - constructor Create(); - destructor Destroy();override; - - procedure Add(aStr : string); - procedure Clear(); - - function GetPCharArray() : PAnsiDACChar; - end; - - - TLogEvent = procedure (Sender: TObject; const LogMessage: string) of object; - - TLibraryLoadEvent = procedure (Sender: TObject; var FileName: string) of object; - - EPSQLDumpException = class(Exception); - - TDumpRestoreSection = (drsPreData, drsData, drsPostData); - - TDumpRestoreSections = set of TDumpRestoreSection; - - TDumpOption = (doDataOnly, doIncludeBLOBs, doClean, doCreate, doInserts, - doColumnInserts, doIgnoreVersion, doOIDs, doNoOwner, - doSchemaOnly, doVerbose, doNoPrivileges, doDisableDollarQuoting, - doDisableTriggers, doUseSetSessionAuthorization, doNoTablespaces, - doQuoteAllIdentifiers, doNoSecurityLabels, doNoUnloggedTableData, - doSerializableDeferrable, doNoSynchronizedSnapshots, doIfExists, - doEnableRowSecurity, doStrictNames, doNoBlobs, doNoSync, - doNoSubscriptions, doNoPublications, doLoadViaPartitionRoot, - doNoComments); - - TDumpOptions = set of TDumpOption; - - TDumpStrOption = (dsoSchema, dsoSuperuser, dsoTable, dsoExcludeSchema, - dsoExcludeTable, dsoEncoding, dsoRole, dsoExcludeTableData, - dsoSnapshot); - - TDumpFormat = (dfPlain, dfTarArchive, dfCompressedArchive, dfDirectory); - - TCompressLevel = 0..9; - - TPSQLDump = class(TComponent) - private - FAbout : TPSQLDACAbout; - {$IFDEF M_DEBUG} - FParamStr : string; - {$ENDIF} - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; - FCompressLevel: TCompressLevel; - FDumpFormat : TDumpFormat; - FDumpOptions : TDumpOptions; - FDumpStrOptions : array[TDumpStrOption] of string; - FBeforeDump : TNotifyEvent; - FAfterDump : TNotifyEvent; - FRewriteFile: boolean; - FmiParams: TpdmvmParams; - FTableNames: TStrings; - FSchemaNames: TStrings; - FExcludeTables: TStrings; - FExcludeSchemas: TStrings; - FOnLog: TLogEvent; - FLockWaitTimeout: cardinal; - FOnLibraryLoad: TLibraryLoadEvent; - FJobs: cardinal; - FExcludeTablesData: TStrings; - FSections: TDumpRestoreSections; - procedure SetDatabase(const Value : TPSQLDatabase); - procedure SetCompressLevel(const Value: TCompressLevel); - function GetStrOptions(const Index: Integer): string; - procedure SetStrOptions(const Index: Integer; const Value: string); - procedure Dump(const TargetFile, LogFile: string); - procedure SetTableNames(const Value: TStrings); - procedure SetSchemaNames(const Value: TStrings); - procedure SetExcludeSchemas(const Value: TStrings); - procedure SetExcludeTables(const Value: TStrings); - procedure DoLog(const Value: string); - function GetVersionAsInt: integer; - function GetVersionAsStr: string; - procedure ReadTableName(Reader: TReader); //deal with old missing properties - procedure ReadSchemaName(Reader: TReader); //deal with old missing properties - procedure SetLockWaitTimeout(const Value: cardinal); - procedure SetDumpOptions(const Value: TDumpOptions); - procedure SetExcludeTablesData(const Value: TStrings); - protected - procedure CheckDependencies; - procedure DefineProperties(Filer: TFiler); override; - function GetParameters(OutputFileName: string): PAnsiDACChar; - procedure Notification( AComponent: TComponent; Operation: TOperation ); Override; - public - constructor Create(Owner : TComponent); override; - destructor Destroy; override; - procedure DumpToStream(Stream: TStream); overload; - procedure DumpToStream(Stream: TStream; Log: TStrings); overload; - procedure DumpToStream(Stream: TStream; LogFileName: string); overload; - procedure DumpToFile(const FileName: string; Log: TStrings); overload; - procedure DumpToFile(const FileName, LogFileName: string); overload; - property VersionAsInt: integer read GetVersionAsInt; - property VersionAsStr: string read GetVersionAsStr; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property CompressLevel: TCompressLevel read FCompressLevel write SetCompressLevel default 0; - property Database : TPSQLDatabase read FDatabase write SetDatabase; - property DumpFormat : TDumpFormat read FDumpFormat write FDumpFormat default dfPlain; - property Encoding: string index dsoEncoding read GetStrOptions write SetStrOptions; - property ExcludeSchemas: TStrings read FExcludeSchemas write SetExcludeSchemas; - property ExcludeTables: TStrings read FExcludeTables write SetExcludeTables; - property ExcludeTablesData: TStrings read FExcludeTablesData write SetExcludeTablesData; - property LockWaitTimeout: cardinal read FLockWaitTimeout write SetLockWaitTimeout default 0; - property Options : TDumpOptions read FDumpOptions write SetDumpOptions default []; - property RewriteFile: boolean read FRewriteFile write FRewriteFile default True; - property Role: string index dsoRole read GetStrOptions write SetStrOptions; - property SchemaNames: TStrings read FSchemaNames write SetSchemaNames; - property SuperUserName: string index dsoSuperUser read GetStrOptions write SetStrOptions; - property TableNames: TStrings read FTableNames write SetTableNames; - property Jobs: cardinal read FJobs write FJobs; - property Sections: TDumpRestoreSections read FSections write FSections; - property Snapshot: string index dsoSnapshot read GetStrOptions write SetStrOptions; - property AfterDump : TNotifyEvent read FAfterDump write FAfterDump; - property BeforeDump : TNotifyEvent read FBeforeDump write FBeforeDump; - property OnLog: TLogEvent read FOnLog write FOnLog; - property OnLibraryLoad: TLibraryLoadEvent read FOnLibraryLoad write FOnLibraryLoad; - end; - - -{TPSQLRestore stuff} - EPSQLRestoreException = class(Exception); - - TRestoreFormat = (rfAuto, rfTarArchive, rfCompressedArchive, rfDirectory); - - TRestoreOption = (roDataOnly, roClean, roCreate, roExitOnError, roIgnoreVersion, - roList, roNoOwner, roSchemaOnly, roVerbose, roNoPrivileges, - roDisableTriggers, roUseSetSessionAuthorization, roSingleTransaction, - roNoDataForFailedTables, roNoTablespaces, roNoSecurityLabels, roIfExists, - roEnableRowSecurity, roStrictNames, roNoSubscriptions, roNoPublications); - - TRestoreOptions = set of TRestoreOption; - - TRestoreStrOption = (rsoTable, rsoSuperUser, rsoDBName, - rsoFileName, rsoIndex, rsoListFile, rsoFunction, - rsoTrigger, rsoRole, rsoSchemaName, rsoExcludeSchema); - - TPSQLRestore = class(TComponent) - private - FAbout: TPSQLDACAbout; - {$IFDEF M_DEBUG} - FParamStr: string; - {$ENDIF} - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; - FRestoreFormat: TRestoreFormat; - FRestoreOptions: TRestoreOptions; - FRestoreStrOptions: array [TRestoreStrOption] of string; - FBeforeRestore: TNotifyEvent; - FAfterRestore: TNotifyEvent; - FmiParams: TpdmvmParams; - FOnLog: TLogEvent; - FJobs: cardinal; - FOnLibraryLoad: TLibraryLoadEvent; - FTableNames: TStrings; - FSections: TDumpRestoreSections; - FSchemaNames: TStrings; - FExcludeSchemas: TStrings; - procedure SetDatabase(const Value : TPSQLDatabase); - function GetStrOptions(const AnIndex: Integer): string; - procedure SetStrOptions(const AnIndex: Integer; const Value: string); - procedure Restore(const SourceFile, LogFile: string); - procedure DoLog(const Value: string); - function GetVersionAsInt: integer; - procedure SetJobs(const Value: cardinal); - procedure SetRestoreOptions(const Value: TRestoreOptions); - function GetVersionAsStr: string; - procedure SetTableNames(const Value: TStrings); - procedure ReadTableName(Reader: TReader); - procedure ReadSchemaName(Reader: TReader); - procedure SetSchemaNames(const Value: TStrings); - procedure SetExcludeSchemas(const Value: TStrings); - protected - procedure CheckDependencies; - procedure DefineProperties(Filer: TFiler); override; - function GetParameters(SourceFileName: string): PAnsiDACChar; - procedure Notification( AComponent: TComponent; Operation: TOperation ); Override; - public - constructor Create(Owner : TComponent); override; - destructor Destroy; override; - procedure RestoreFromFile(const FileName: string; Log: TStrings); overload; - procedure RestoreFromFile(const FileName, LogFileName: string); overload; - property VersionAsInt: integer read GetVersionAsInt; - property VersionAsStr: string read GetVersionAsStr; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property Database : TPSQLDatabase read FDatabase write SetDatabase; - property DBName: string index rsoDBName read GetStrOptions write SetStrOptions; - property FunctionDecl: string index rsoFunction read GetStrOptions write SetStrOptions; - property Index: string index rsoIndex read GetStrOptions write SetStrOptions; - property Jobs: cardinal read FJobs write SetJobs; - property ListFile: string index rsoListFile read GetStrOptions write SetStrOptions; - property Options : TRestoreOptions read FRestoreOptions write SetRestoreOptions; - property OutputFileName: string index rsoFileName read GetStrOptions write SetStrOptions; - property RestoreFormat : TRestoreFormat read FRestoreFormat write FRestoreFormat; - property Role: string index dsoRole read GetStrOptions write SetStrOptions; - property SuperUserName: string index rsoSuperUser read GetStrOptions write SetStrOptions; - property Trigger: string index rsoTrigger read GetStrOptions write SetStrOptions; - property TableNames: TStrings read FTableNames write SetTableNames; - property SchemaNames: TStrings read FSchemaNames write SetSchemaNames; - property ExcludeSchemas: TStrings read FExcludeSchemas write SetExcludeSchemas; - property Sections: TDumpRestoreSections read FSections write FSections; - property AfterRestore : TNotifyEvent read FAfterRestore write FAfterRestore; - property BeforeRestore : TNotifyEvent read FBeforeRestore write FBeforeRestore; - property OnLog: TLogEvent read FOnLog write FOnLog; - property OnLibraryLoad: TLibraryLoadEvent read FOnLibraryLoad write FOnLibraryLoad; - end; - -const - DumpRestoreCommandLineSectionParameters: array[TDumpRestoreSection] of string = ( - '--section=pre-data', //drsPreData - '--section=data', //drsData - '--section=post-data' //drsPostData - ); - - DumpCommandLineBoolParameters: array[TDumpOption] of string = ( - '--data-only', //doDataOnly - '--blobs', //doIncludeBLOBs - '--clean', //doClean - '--create', //doCreate - '--inserts', //doInserts - '--column-inserts', //doColumnInserts, aka '--attribute-inserts' - '--ignore-version', //doIgnoreVersion - '--oids', //doOIDs - '--no-owner', //doNoOwner - '--schema-only', //doSchemaOnly - '--verbose', //doVerbose, - '--no-privileges', //doNoPrivileges, aka '--no-acl' - '--disable-dollar-quoting', //doDisableDollarQuoting, - '--disable-triggers', //doDisableTriggers, - '--use-set-session-authorization', //doUseSetSessionAuthorization, - '--no-tablespaces', //doNoTablespaces - '--quote-all-identifiers', //doQuoteAllIdentifiers - '--no-security-labels', //doNoSecurityLabels - '--no-unlogged-table-data', //doNoUnloggedTableData - '--serializable-deferrable', //doSerializableDeferrable - '--no-synchronized-snapshots', //doNoSynchronizedSnapshots - '--if-exists', //doIfExists - '--enable-row-security', //doEnableRowSecurity - '--strict-names', //doStrictNames - '--no-blobs', //doNoBlobs - '--no-sync', //doNoSync - '--no-subscriptions', //doNoSubscriptions - '--no-publications', //doNoPublications - '--load-via-partition-root', //doLoadViaPartitionRoot - '--no-comments' //doNoComments - - - ); - - RestoreCommandLineBoolParameters: array[TRestoreOption] of string = ( - //--no-reconnect obsolete - '--data-only', //roDataOnly - '--clean', //roClean - '--create', //roCreate - '--exit-on-error', //roExitOnError - '--ignore-version', //roIgnoreVersion - '--list', //roList, aka '--no-acl' - '--no-owner', //roNoOwner - '--schema-only', //roSchemaOnly - '--verbose', //roVerbose, - '--no-privileges', //roNoPrivileges, - '--disable-triggers', //roDisableTriggers, - '--use-set-session-authorization', //roUseSetSessionAuthorization - '--single-transaction', //roSingleTransaction - '--no-data-for-failed-tables', //roNoDataForFailedTables - '--no-tablespaces', //roNoTablespaces - '--no-security-labels', //roNoSecurityLabels - '--if-exists', //roIfExists - '--enable-row-security', //roEnableRowSecurity - '--strict-names', //roStrictNames - '--no-subscriptions', //roNoSubscriptions - '--no-publications' //roNoPublications - ); - - DumpCommandLineStrParameters: array[TDumpStrOption] of string =( - '--schema=', //dsoSchema - '--superuser=', //dsoSuperuser - '--table=', //dsoTable - '--exclude-schema=', //dsoExcludeSchema - '--exclude-table=', //dsoExcludeTable - '--encoding=', //dsoEncoding - '--role=', //dsoRole - '--exclude-table-data=', //dsoExcludeTableData - '--snapshot=' //dsoSnapshot - ); - - RestoreCommandLineStrParameters: array[TRestoreStrOption] of string =( - '--table=', //rsoTable - '--superuser=', //rsoSuperuser - '--dbname=', //rsoDBName, - '--file=', //rsoFileName, - '--index=', //rsoIndex, - '--use-list=', //rsoListFile, - '--function=', //rsoFunction, - '--trigger=', //rsoTrigger - '--role=', //dsoRole - '--schema=', //rsoSchemaName - '--exclude-schema=' //rsoExcludeSchema - ); - - - DumpCommandLineFormatValues: array[TDumpFormat] of string = ( - '--format=p', //plain - '--format=t', //tar archive - '--format=c', //custom archive - '--format=d' //directory - ); - - RestoreCommandLineFormatValues: array[TRestoreFormat] of string = ( - '', //auto - '--format=t', //tar archive - '--format=c', //custom archive - '--format=d' //directory - ); - -function GetTempFileName: string; - -implementation - -uses PSQLAccess, -{$IFDEF MSWINDOWS} - Windows -{$ENDIF} -{$IFDEF POSIX} - Posix.SysTypes, Posix.Stdio, Posix.Stdlib -{$ENDIF}; - -var ProccessOwner: TComponent = nil; - -function IntVerToStr(VerInt: integer): string; -var Major, Minor, Revision: integer; -begin - Major := VerInt div 10000; - Minor := VerInt mod 10000 div 100; - Revision := VerInt mod 100; - Result := Format('%d.%d.%d', [Major, Minor, Revision]); -end; - -function StringToAnsiChar(CodePage: Cardinal; Src: {$IFNDEF NEXTGEN}WideString{$ELSE}String{$ENDIF}): PAnsiDACChar; -var - Len: integer; -begin - Result := nil; - if Src = EmptyStr then Exit; - Len := {$IFDEF DELPHI_16} - LocaleCharsFromUnicode - {$ELSE} - WideCharToMultiByte - {$ENDIF}(codePage, 0, PWideChar(Src), Length(Src), nil, 0, nil, nil); - if Len = 0 then Exit; - GetMem(Result, Len + 1); - if {$IFDEF DELPHI_16} - LocaleCharsFromUnicode - {$ELSE} - WideCharToMultiByte - {$ENDIF}(codePage, 0, PWideChar(Src), Length(Src), PAnsiDACChar(Result), Len, nil, nil) = 0 then - ReallocMem(Result, 0) - else - Result[Len] := #0; -end; - -function GetTempPath: string; -{$IFDEF MSWINDOWS} -var - Len: Integer; -begin - SetLastError(ERROR_SUCCESS); - - // get memory for the buffer retaining the temp path (plus null-termination) - SetLength(Result, MAX_PATH); - Len := Windows.GetTempPath(MAX_PATH, PChar(Result)); - if Len = 0 then - Result := ''; -end; -{$ENDIF} -{$IFDEF POSIX} -const - CEnvVars: array[0..2] of DACAString = ('TMPDIR', 'TMP', 'TEMP'); // Do not localize - CTmpDir = '/tmp'; // Do not localize - -var - LTempPathVar: PAnsiDACChar; - I: Integer; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - { Lookup env variables, in order: TMPDIR, TMP, TEMP } - for I := Low(CEnvVars) to High(CEnvVars) do - begin - LTempPathVar := getenv({$IFNDEF NEXTGEN} - PAnsiChar(CEnvVars[I]) - {$ELSE} - M.AsAnsi(CEnvVars[I]).ToPointer - {$ENDIF} - ); - - if (LTempPathVar <> nil) and (LTempPathVar^ <> #0) then - begin - { We have found our temporary path } - Break; - end; - end; - - { Get the UTF16 value out of the UTF8 one. The last resort is to fallback to /tmp } - if LTempPathVar <> nil then - Result := UTF8ToUnicodeString(LTempPathVar) - else - Result := CTmpDir; -end; -{$ENDIF} - -function GetTempFileName: string; -{$IFDEF MSWINDOWS} -var - TempPath: string; - ErrCode: UINT; -begin - TempPath := GetTempPath(); - SetLength(Result, MAX_PATH); - - SetLastError(ERROR_SUCCESS); - ErrCode := Windows.GetTempFileName(PChar(TempPath), 'tmp', 0, PChar(Result)); // DO NOT LOCALIZE - if ErrCode = 0 then - raise EInOutError.Create(SysErrorMessage(GetLastError)); - - SetLength(Result, StrLen(PChar(Result))); -end; -{$ENDIF} -{$IFDEF POSIX} -var - LTempPath: PAnsiDACChar; -begin - { Obtain a temporary file name } - LTempPath := tmpnam(nil); - - { Convert to UTF16 or leave blank on possible error } - if LTempPath <> nil then - Result := UTF8ToUnicodeString(LTempPath) - else - Result := ''; -end; -{$ENDIF} - -{ TpdmvmParams } -procedure TpdmvmParams.Add(aStr: string); -begin - FParams.Add(aStr); -end; - -procedure TpdmvmParams.Clear; -begin - FParams.Clear(); -end; - -procedure TpdmvmParams.ClearMem; -var - s : PAnsiDACChar; - p : PInteger; //32-bit pointer -begin - if FArr <> nil then - begin - p := Pointer(FArr); - repeat - s := PAnsiDACChar(p^); - if s <> nil then - FreeMem(s); - Inc(p); - until s = nil; - FreeMem(FArr); - FArr := nil; - end; -end; - -constructor TpdmvmParams.Create; -begin - FParams := TStringList.Create(); - FArr := nil; -end; - -destructor TpdmvmParams.Destroy; -begin - ClearMem(); - FParams.Free(); - - inherited; -end; - -function TpdmvmParams.GetPcharArray: PAnsiDACChar; -var - s : PAnsiDACChar; - p : PInteger; //32-bit pointer - i : integer; -begin - ClearMem(); - - GetMem(FArr, SizeOf(PAnsiDACChar) * (FParams.Count + 1)); - p := Pointer(FArr); - for i:=0 to FParams.Count - 1 do - begin - if FParams.Names[i] = '--file' then - S := StringToAnsiChar(CP_ACP, FParams[i]) - else - S := StringToAnsiChar(CP_UTF8, FParams[i]); - p^ := Integer(s); - Inc(p); - end; - p^ := 0; - Result := FArr; -end; - -{TPSQLDump} - -Constructor TPSQLDump.Create(Owner : TComponent); -var I: integer; -begin - inherited Create(Owner); - FDumpOptions := []; - {$IFNDEF NEXTGEN} - ZeroMemory(@FDumpStrOptions,sizeof(FDumpStrOptions)); - {$ELSE} - FillChar(FDumpStrOptions, sizeof(FDumpStrOptions), 0); - {$ENDIF} - FRewriteFile := True; - FTableNames := TStringList.Create; - FExcludeTables := TStringList.Create; - FExcludeTablesData := TStringList.Create; - FExcludeSchemas := TStringList.Create; - FSchemaNames := TStringList.Create; - FmiParams := TpdmvmParams.Create; - if (csDesigning in ComponentState) and Assigned(Owner) then - for I := Owner.ComponentCount - 1 downto 0 do - if Owner.Components[I] is TPSQLDatabase then - begin - Database := Owner.Components[I] as TPSQLDatabase; - Break; - end; -end; - -destructor TPSQLDump.Destroy; -begin - FmiParams.Free; - FTableNames.Free; - FSchemaNames.Free; - FExcludeTables.Free; - FExcludeTablesData.Free; - FExcludeSchemas.Free; - inherited Destroy; -end; - -Procedure TPSQLDump.Notification( AComponent: TComponent; Operation: TOperation ); -begin - Inherited Notification(AComponent, Operation); - if (Operation = opRemove) and (AComponent = FDatabase) then - FDatabase := nil; -end; - -procedure TPSQLDump.SetDatabase(const Value : TPSQLDatabase); -begin - if Value <> FDatabase then - FDatabase := Value; -end; - -procedure TPSQLDump.SetCompressLevel(const Value: TCompressLevel); -begin - FCompressLevel := Value; -end; - -procedure TPSQLDump.DumpToFile(const FileName: string; Log: TStrings); -var - tmpLogFile: string; -begin - tmpLogFile := GetTempFileName(); - if tmpLogFile = '' then - raise EPSQLDumpException.Create('Can''t create temporary log file'); - try - DumpToFile(FileName, tmpLogFile); - finally - if Assigned(Log) then - Log.LoadFromFile(tmpLogFile); - SysUtils.DeleteFile(tmpLogFile); - end; -end; - -procedure TPSQLDump.DumpToStream(Stream: TStream); -begin - DumpToStream(Stream,nil); -end; - -procedure TPSQLDump.CheckDependencies; -begin - if not Assigned(FDatabase) then - raise EDatabaseError.Create('Property Database not set!'); - if not FDatabase.Connected then - FDatabase.Connected := True; - - if (DumpFormat <> dfDirectory) and (FJobs > 1) then - raise EPSQLRestoreException.Create('Multiple jobs can be used only with directory output format'); -{$IFDEF MSWINDOWS} - if FJobs > MAXIMUM_WAIT_OBJECTS then - raise EPSQLRestoreException.CreateFmt('Maximum number of parallel jobs is %d',[MAXIMUM_WAIT_OBJECTS]); -{$ENDIF} - - if [doDataOnly, doSchemaOnly] * FDumpOptions = [doDataOnly, doSchemaOnly] then - raise EPSQLDumpException.Create('Options "Schema only" and "Data only" cannot be used together'); - if [doDataOnly, doClean] * FDumpOptions = [doDataOnly, doClean] then - raise EPSQLDumpException.Create('Options "Clean" and "Data only" cannot be used together'); - if (doIncludeBLOBs in FDumpOptions) and (FDumpStrOPtions[dsoTable] > '') then - raise EPSQLDumpException.Create('Large-object output not supported for a single table.'#13#10+ - 'Use a full dump instead'); - if (doIncludeBLOBs in FDumpOptions) and (FDumpStrOPtions[dsoSchema] > '') then - raise EPSQLDumpException.Create('Large-object output not supported for a single schema.'#13#10+ - 'Use a full dump instead'); - if [doInserts, doOids] * FDumpOPtions = [doInserts, doOids] then - raise EPSQLDumpException.Create('"Insert" and "OID" options cannot be used together.'#13#10+ - 'The INSERT command cannot set OIDs'); - if (doIncludeBLOBs in FDumpOptions) and (FDumpFormat = dfPlain) then - raise EPSQLDumpException.Create('Large-object output is not supported for plain-text dump files.'#13#10+ - 'Use a different output format.'); -end; - -function TPSQLDump.GetParameters(OutputFileName: string): PAnsiDACChar; -var I: TDumpOption; - J: TDumpStrOption; - DRS: TDumpRestoreSection; - k: integer; -begin - if not Assigned(FDatabase) then - raise EPSQLDumpException.Create('Database property not assigned!'); - - FmiParams.Clear; - - FmiParams.Add('--file=' + OutputFileName); - - for I := Low(TDumpOption) to High(TDumpOption) do - if I in FDumpOptions then - FmiParams.Add(DumpCommandLineBoolParameters[I]); - - for J := Low(TDumpStrOption) to High(TDumpStrOption) do - if FDumpStrOptions[J] > '' then - FmiParams.Add(DumpCommandLineStrParameters[J] + FDumpStrOptions[J]); - - for DRS := Low(TDumpRestoreSection) to High(TDumpRestoreSection) do - if DRS in FSections then - FmiParams.Add(DumpRestoreCommandLineSectionParameters[DRS]); - - for k := 0 to FSchemaNames.Count-1 do - FmiParams.Add(DumpCommandLineStrParameters[dsoSchema] + FSchemaNames[k]); - - for k := 0 to FExcludeSchemas.Count-1 do - FmiParams.Add(DumpCommandLineStrParameters[dsoExcludeSchema] + FExcludeSchemas[k]); - - for k := 0 to FTableNames.Count-1 do - FmiParams.Add(DumpCommandLineStrParameters[dsoTable] + FTableNames[k]); - - for k := 0 to FExcludeTables.Count-1 do - FmiParams.Add(DumpCommandLineStrParameters[dsoExcludeTable] + FExcludeTables[k]); - - for k := 0 to FExcludeTablesData.Count-1 do - FmiParams.Add(DumpCommandLineStrParameters[dsoExcludeTableData] + FExcludeTablesData[k]); - - FmiParams.Add(DumpCommandLineFormatValues[FDumpFormat]); - - if FDumpFormat in [dfCompressedArchive, dfPlain] then - FmiParams.Add(Format('--compress=%d', [FCompressLevel])); - - if FLockWaitTimeout > 0 then - FmiParams.Add(Format('--lock-wait-timeout=%u', [FLockWaitTimeout])); - - if FJobs > 1 then - FmiParams.Add(Format('--jobs=%u', [FJobs])); - - FmiParams.Add('--no-password'); //we will put it using environment variable - - with FDatabase do - begin - FmiParams.Add(Format('--username=%s',[UserName])); - FmiParams.Add(Format('--port=%d',[Port])); - FmiParams.Add(Format('--host=%s',[Host])); - FmiParams.Add(DatabaseName); - end; - - Result := FmiParams.GetPCharArray(); - - {$IFDEF M_DEBUG} - FParamStr := FmiParams.FParams.Commatext; - {$ENDIF} -end; - -function TPSQLDump.GetStrOptions(const Index: Integer): string; -begin - Result := FDumpStrOptions[TDumpStrOption(Index)]; -end; - -procedure TPSQLDump.SetStrOptions(const Index: Integer; - const Value: string); -begin - FDumpStrOptions[TDumpStrOption(Index)] := Value; -end; - -procedure TPSQLDump.DumpToFile(const FileName, LogFileName: string); -begin - CheckDependencies; - - if Assigned(FBeforeDump) then - FBeforeDump(Self); - - Dump(FileName, LogFileName); - - If Assigned(FAfterDump) then - FAfterDump(Self); -end; - -procedure TPSQLDump.DumpToStream(Stream: TStream; Log: TStrings); -var - tmpLogFile: string; -begin - tmpLogFile := GetTempFileName(); - if Assigned(Log) then - if tmpLogFile = '' then - raise EPSQLDumpException.Create('Can''t create temporary log file'); - try - DumpToStream(Stream,tmpLogFile); - finally - if Assigned(Log) then - begin - Log.LoadFromFile(tmpLogFile); - SysUtils.DeleteFile(tmpLogFile); - end; - end; -end; - -procedure TPSQLDump.DumpToStream(Stream: TStream; LogFileName: string); -var - tmpTargetFile: string; - FS: TFileStream; -begin - tmpTargetFile := GetTempFileName(); - if tmpTargetFile = '' then - raise EPSQLDumpException.Create('Can''t create temporary target file'); - try - DumpToFile(tmpTargetFile,LogFileName); - if Assigned(Stream) then - begin - FS := TFileStream.Create(tmpTargetFile,fmOpenRead); - try - Stream.CopyFrom(FS,FS.Size); - finally - FS.Free; - end; - end; - finally - SysUtils.DeleteFile(tmpTargetFile); - end; -end; - -procedure ErrorCallBackProc(Code: integer);cdecl; -begin - {This callback ALWAYS must raise an exception! - In any way dump or restore process will be aborted} - raise EPSQLDumpException.Create(Format('Error with code: %d', [Code])); -end; - -procedure LogCallBackProc(S: PAnsiDACChar);cdecl; -begin - if Assigned(ProccessOwner) then - if (ProccessOwner is TPSQLDump) then - (ProccessOwner as TPSQLDump).DoLog(TrimRight(UTF8ToString(S))) - else - if (ProccessOwner is TPSQLRestore) then - (ProccessOwner as TPSQLRestore).DoLog(TrimRight(UTF8ToString(S))); -end; - -{$IFDEF UNDER_DELPHI_6} -function DirectoryExists(const Name: string): Boolean; -var - Code: Integer; -begin - Code := GetFileAttributes(PChar(Name)); - Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); -end; -{$ENDIF} - -{$IFDEF DELPHI_5} -type UTF8String = AnsiString; -{$ENDIF} - -procedure UpdateEnv(PWD: {$IFNDEF NEXTGEN}UTF8String{$ELSE}String{$ENDIF}); -{$IFDEF MSWINDOWS} -type - tputenv = function(NameValue: PAnsiDACChar): integer; cdecl; -var - putenv: tputenv; -{$ENDIF} -{$IFDEF NEXTGEN} -var - M: TMarshaller; -{$ENDIF} -begin -{$IFDEF MSWINDOWS} - @putenv := GetProcAddress(GetModuleHandle('msvcrt'), '_putenv'); - if not Assigned(putenv) then - raise EPSQLDumpException.Create('Cannot obtain _putenv procedure entry point'); - if putenv(PAnsiChar('PGPASSWORD=' + PWD)) <> 0 then - raise EPSQLDumpException.Create('Cannot populate environment settings'); - if not SetEnvironmentVariableA('PGPASSWORD', PAnsiChar(PWD)) then - {$IFDEF DELPHI_5} - RaiseLastWin32Error(); - {$ELSE} - RaiseLastOSError(); - {$ENDIF} -{$ELSE POSIX} - SetEnv('PGPASSWORD', {$IFNDEF NEXTGEN}PAnsiChar(PWD){$ELSE}M.AsAnsi(PWD).ToPointer{$ENDIF}, 1); -{$ENDIF} -end; - -procedure TPSQLDump.Dump(const TargetFile, LogFile: string); -var - h : Cardinal; - Result: longint; - S: string; - - PLog: PAnsiDACChar; - Params: PAnsiDACChar; - - v3_dump: Tv3_dump; - pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; - pdmbvm_SetErrorCallBackProc : Tpdmbvm_SetErrorCallBackProc; - pdmbvm_SetLogCallBackProc : Tpdmbvm_SetLogCallBackProc; - - LibName: string; -begin - if (FDumpFormat = dfDirectory) then - begin - if DirectoryExists(TargetFile) then - raise EPSQLDumpException.Create('Cannot create dump. Target directory exists: ' + TargetFile); - end - else - if FileExists(TargetFile) and not FRewriteFile then - raise EPSQLDumpException.Create('Cannot create dump. Target file exists: ' + TargetFile); - - LibName := 'pg_dump.dll'; - if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); - h := SafeLoadLibrary(PChar(LibName)); - {$IFDEF M_DEBUG} - LogDebugMessage('DUMPLIB', GetModuleName(h)); - {$ENDIF} - try - @v3_dump := GetProcAddress(h, PChar('v3_dump')); - if not assigned(@v3_dump) then - raise EPSQLDumpException.Create('Can''t load pg_dump.dll'); - - @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); - - {$IFDEF M_DEBUG} - LogDebugMessage('DUMPVER', IntToStr(pdmbvm_GetVersionAsInt())); - {$ENDIF} - - @pdmbvm_SetErrorCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetErrorCallBackProc')); - @pdmbvm_SetLogCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetLogCallBackProc')); - - pdmbvm_SetErrorCallBackProc(@ErrorCallBackProc); - if Assigned(FOnLog) then - begin - ProccessOwner := Self; - pdmbvm_SetLogCallBackProc(@LogCallBackProc); - end; - PLog := StringToAnsiChar(CP_ACP, LogFile); - UpdateEnv({$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(FDatabase.UserPassword))); - Params := GetParameters(TargetFile); - {$IFDEF M_DEBUG} - LogDebugMessage('PARAMSTR', FParamStr); - {$ENDIF} - Result := v3_dump(PAnsiDACChar(UTF8Encode(ParamStr(0))), PLog, Params); - - case Result of - 0: S := ''; - 1: S := 'Common dump error'; - 2: S := 'Connection error (wrong username, password, host etc.)'; - 3: S := 'File IO error'; - else - S := 'Uknown dump error'; - end; - if S > '' then - raise EPSQLDumpException.Create(S + #13#10 + Format('Error Code: %d', [Result])); - - finally - ProccessOwner := nil; - ReallocMem(PLog, 0); - FreeLibrary(h); - end; -end; - -procedure TPSQLDump.SetTableNames(const Value: TStrings); -begin - FTableNames.Assign(Value); -end; - -procedure TPSQLDump.SetSchemaNames(const Value: TStrings); -begin - FSchemaNames.Assign(Value); -end; - -procedure TPSQLDump.SetExcludeSchemas(const Value: TStrings); -begin - FExcludeSchemas.Assign(Value); -end; - -procedure TPSQLDump.SetExcludeTables(const Value: TStrings); -begin - FExcludeTables.Assign(Value); -end; - -procedure TPSQLDump.SetExcludeTablesData(const Value: TStrings); -begin - FExcludeTablesData.Assign(Value); -end; - -procedure TPSQLDump.DoLog(const Value: string); -begin - if Assigned(FOnLog) then - FOnLog(Self, Value); -end; - -function TPSQLDump.GetVersionAsInt: integer; -var - h : Cardinal; - LibName: string; - pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; -begin - Result := 0; - LibName := 'pg_dump.dll'; - if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); - h := LoadLibrary(PChar(LibName)); - try - @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); - if Assigned(pdmbvm_GetVersionAsInt) then - Result := pdmbvm_GetVersionAsInt(); - finally - FreeLibrary(H); - end; -end; - -function TPSQLDump.GetVersionAsStr: string; -begin - Result := IntVerToStr(GetVersionAsInt()); -end; - -procedure TPSQLDump.DefineProperties(Filer: TFiler); -begin - inherited; - Filer.DefineProperty('TableName', ReadTableName, nil, False); - Filer.DefineProperty('SchemaName', ReadSchemaName, nil, False); -end; - -procedure TPSQLDump.ReadSchemaName(Reader: TReader); -var S: string; -begin - S := Reader.ReadString; - if S > '' then FSchemaNames.Append(S); -end; - -procedure TPSQLDump.ReadTableName(Reader: TReader); -var S: string; -begin - S := Reader.ReadString; - if S > '' then FTableNames.Append(S); -end; - -procedure TPSQLDump.SetLockWaitTimeout(const Value: cardinal); -begin - FLockWaitTimeout := Value; -end; - -procedure TPSQLDump.SetDumpOptions(const Value: TDumpOptions); -begin - FDumpOptions := Value; - // since we allow use of custom lib this is not correct anymore - // Exclude(FDumpOptions, doIgnoreVersion); //deprecated and to be removed -end; - -{TPSQLRestore} - -constructor TPSQLRestore.Create(Owner: TComponent); -var I: integer; -begin - inherited; - FRestoreOptions := []; - FmiParams := TpdmvmParams.Create; - FRestoreFormat := rfAuto; - FTableNames := TStringList.Create; - FSchemaNames := TStringList.Create; - FExcludeSchemas := TStringList.Create; - {$IFNDEF NEXTGEN} - ZeroMemory(@FRestoreStrOptions, SizeOf(FRestoreStrOptions)); - {$ELSE} - FillChar(FRestoreStrOptions, SizeOf(FRestoreStrOptions), 0); - {$ENDIF} - if (csDesigning in ComponentState) and Assigned(Owner) then - for I := Owner.ComponentCount - 1 downto 0 do - if Owner.Components[I] is TPSQLDatabase then - begin - Database := Owner.Components[I] as TPSQLDatabase; - Break; - end; -end; - -procedure TPSQLRestore.DefineProperties(Filer: TFiler); -begin - inherited; - Filer.DefineProperty('TableName', ReadTableName, nil, False); - Filer.DefineProperty('SchemaName', ReadSchemaName, nil, False); -end; - -destructor TPSQLRestore.Destroy; -begin - FmiParams.Free; - FTableNames.Free; - FSchemaNames.Free; - FExcludeSchemas.Free; - inherited; -end; - -procedure TPSQLRestore.RestoreFromFile(const FileName: string; Log: TStrings); -var tmpLogFile: string; -begin - tmpLogFile := GetTempFileName(); - if tmpLogFile = '' then - raise EPSQLRestoreException.Create('Can''t create temporary log file'); - try - RestoreFromFile(FileName,tmpLogFile); - finally - If Assigned(Log) then - Log.LoadFromFile(tmpLogFile); - SysUtils.DeleteFile(tmpLogFile); - end; -end; - -procedure TPSQLRestore.RestoreFromFile(const FileName, LogFileName: string); -begin - CheckDependencies; - - if Assigned(FBeforeRestore) then - FBeforeRestore(Self); - - Restore(FileName, LogFileName); - - if Assigned(FAfterRestore) then - FAfterRestore(Self); -end; - -function TPSQLRestore.GetParameters(SourceFileName: string): PAnsiDACChar; -var I: TRestoreOption; - J: TRestoreStrOption; - K: Integer; - DRS: TDumpRestoreSection; -begin - if not Assigned(FDatabase) then - raise EPSQLRestoreException.Create('Database property not assigned!'); - - FmiParams.Clear; - - for I := Low(TRestoreOption) to Pred(High(TRestoreOption)) do - if I in FRestoreOptions then - FmiParams.Add(RestoreCommandLineBoolParameters[I]); - - for J := Low(TRestoreStrOption) to High(TRestoreStrOption) do - if (FRestoreStrOptions[J] > '') then - FmiParams.Add(RestoreCommandLineStrParameters[J] + FRestoreStrOPtions[J]); - - for DRS := Low(TDumpRestoreSection) to High(TDumpRestoreSection) do - if DRS in FSections then - FmiParams.Add(DumpRestoreCommandLineSectionParameters[DRS]); - - if FRestoreFormat <> rfAuto then - FmiParams.Add(RestoreCommandLineFormatValues[FRestoreFormat]); - - for k := 0 to FSchemaNames.Count-1 do - FmiParams.Add(RestoreCommandLineStrParameters[rsoSchemaName] + FSchemaNames[k]); - - for k := 0 to FExcludeSchemas.Count-1 do - FmiParams.Add(RestoreCommandLineStrParameters[rsoExcludeSchema] + FExcludeSchemas[k]); - - for k := 0 to FTableNames.Count-1 do - FmiParams.Add(RestoreCommandLineStrParameters[rsoTable] + FTableNames[k]); - - if FJobs > 1 then - FmiParams.Add(Format('--jobs=%u', [FJobs])); - - FmiParams.Add('--no-password'); //we will put it using environment variable - - with FDatabase do - begin - FmiParams.Add(Format('--username=%s',[UserName])); - FmiParams.Add(Format('--port=%d',[Port])); - FmiParams.Add(Format('--host=%s',[Host])); - end; - - FmiParams.Add(SourceFileName); - - Result := FmiParams.GetPCharArray(); - -{$IFDEF M_DEBUG} - FParamStr := FmiParams.FParams.Commatext; -{$ENDIF} -end; - -function TPSQLRestore.GetStrOptions(const AnIndex: Integer): string; -begin - Result := FRestoreStrOptions[TRestoreStrOption(AnIndex)]; -end; - -procedure TPSQLRestore.Notification(AComponent: TComponent; - Operation: TOperation); -begin - Inherited Notification(AComponent, Operation); - if (Operation = opRemove) and (AComponent = FDatabase) then - FDatabase := nil; -end; - -procedure TPSQLRestore.SetDatabase(const Value : TPSQLDatabase); -begin - if Value <> FDatabase then - FDatabase := Value; -end; - -procedure TPSQLRestore.SetExcludeSchemas(const Value: TStrings); -begin - FExcludeSchemas.Assign(Value); -end; - -procedure TPSQLRestore.SetSchemaNames(const Value: TStrings); -begin - FSchemaNames.Assign(Value); -end; - -procedure TPSQLRestore.SetStrOptions(const AnIndex: Integer; - const Value: string); -begin - FRestoreStrOptions[TRestoreStrOption(AnIndex)] := Value; -end; - -procedure TPSQLRestore.SetTableNames(const Value: TStrings); -begin - FTableNames.Assign(Value); -end; - -procedure TPSQLRestore.CheckDependencies; -var CheckDB: TPSQLDatabase; -begin - if not ((FRestoreStrOptions[rsoFileName] > '') xor (FRestoreStrOptions[rsoDBName] > '')) then - raise EPSQLRestoreException.Create('Database or file output should be specified, but not both'); - - - if (roSingleTransaction in FRestoreOptions) and (FJobs > 1) then - raise EPSQLRestoreException.Create('Multiple jobs cannot be used within the single transaction'); - - {$IFDEF MSWINDOWS} - if FJobs > MAXIMUM_WAIT_OBJECTS then - raise EPSQLRestoreException.CreateFmt('Maximum number of parallel jobs is %d',[MAXIMUM_WAIT_OBJECTS]); - {$ENDIF} - - if (FRestoreStrOptions[rsoFileName] = '') - and (FRestoreStrOptions[rsoDBName] = '') - then - Raise EPSQLRestoreException.Create('At least database or file output must be specified'); - - if FRestoreStrOptions[rsoDBName] > '' then - begin - CheckDB := TPSQLDatabase.Create(nil); - try - CheckDB.DatabaseName := FRestoreStrOptions[rsoDBName]; - CheckDB.Host := FDatabase.Host; - CheckDB.Port := FDatabase.Port; - CheckDB.UserName := FDatabase.UserName; - CheckDB.UserPassword := FDatabase.UserPassword; - CheckDB.Connected := True; - finally - CheckDB.Connected := False; - CheckDB.Free; - end; - end; -end; - -procedure TPSQLRestore.ReadSchemaName(Reader: TReader); -var S: string; -begin - S := Reader.ReadString; - if S > '' then FSchemaNames.Append(S); -end; - -procedure TPSQLRestore.ReadTableName(Reader: TReader); -var S: string; -begin - S := Reader.ReadString; - if S > '' then FTableNames.Append(S); -end; - -procedure TPSQLRestore.Restore(const SourceFile, LogFile: string); -var - h : Cardinal; - Result: longint; - S: string; - - v3_restore: Tv3_Restore; - pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; - pdmbvm_SetErrorCallBackProc : Tpdmbvm_SetErrorCallBackProc; - pdmbvm_SetLogCallBackProc : Tpdmbvm_SetLogCallBackProc; - - PLog: PAnsiDACChar; - LibName: string; - Params: PAnsiDACChar; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} -begin - S := ''; - LibName := 'pg_restore.dll'; - if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); - h := SafeLoadLibrary(PChar(LibName)); - {$IFDEF M_DEBUG} - LogDebugMessage('RESTLIB', GetModuleName(h)); - {$ENDIF} - try - @v3_restore := GetProcAddress(h, PChar('v3_restore')); - if not assigned(@v3_restore) then - raise EPSQLRestoreException.Create('Can''t load pg_restore.dll'); - - @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); - - {$IFDEF M_DEBUG} - LogDebugMessage('RESTVER', IntToStr(pdmbvm_GetVersionAsInt())); - {$ENDIF} - - @pdmbvm_SetErrorCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetErrorCallBackProc')); - @pdmbvm_SetLogCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetLogCallBackProc')); - - pdmbvm_SetErrorCallBackProc(@ErrorCallBackProc); - if Assigned(FOnLog) then - begin - ProccessOwner := Self; - pdmbvm_SetLogCallBackProc(@LogCallBackProc); - end; - if LogFile > '' then - {$IFNDEF NEXTGEN} - PLog := PAnsiDACChar(UTF8Encode(LogFile)) - {$ELSE} - PLog := PAnsiDACChar(M.AsAnsi(LogFile)) - {$ENDIF} - else - PLog := nil; - - UpdateEnv({$IFDEF NEXTGEN}string{$ENDIF}(UTF8Encode(FDatabase.UserPassword))); - Params := GetParameters(SourceFile); - - {$IFDEF M_DEBUG} - LogDebugMessage('PARAMSTR', FParamStr); - {$ENDIF} - - Result := v3_restore(PAnsiDACChar(UTF8Encode(ParamStr(0))), PLog, Params); - - case Result of - 0: ;// - OK - 1: if roExitOnError in Options then S := 'Common pg_restore error'; - 3: S := 'Output file error.'; //stdout operation - 4: S := 'Error output file error.'; //stderr - else - S := 'Unknown restore error'; - end; - - if S > '' then - raise EPSQLRestoreException.Create(S + #13#10 + Format('Error Code: %d', [Result])); - - finally - FreeLibrary(h); - end; -end; - -procedure TPSQLRestore.DoLog(const Value: string); -begin - if Assigned(FOnLog) then - FOnLog(Self, Value); -end; - -function TPSQLRestore.GetVersionAsInt: integer; -var - h : Cardinal; - LibName: string; - pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; -begin - Result := 0; - LibName := 'pg_restore.dll'; - if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); - h := LoadLibrary(PChar(LibName)); - try - @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); - if Assigned(pdmbvm_GetVersionAsInt) then - Result := pdmbvm_GetVersionAsInt(); - finally - FreeLibrary(H); - end; -end; -function TPSQLRestore.GetVersionAsStr: string; -begin - Result := IntVerToStr(GetVersionAsInt()); -end; - -procedure TPSQLRestore.SetJobs(const Value: cardinal); -begin - FJobs := Value; -end; - -procedure TPSQLRestore.SetRestoreOptions(const Value: TRestoreOptions); -begin - FRestoreOptions := Value; - //since we allow use of custom lib this is not correct anymore - //Exclude(FRestoreOptions, roIgnoreVersion); -end; - -end. +{$I PSQLDAC.inc} +unit PSQLDump; + +{SVN revision: $Id$} + +interface + +Uses Classes, SysUtils, Db, PSQLTypes, Math, PSQLDbTables; + +type + Tv3_Dump = function (AppName: PAnsiDACChar; LogFileName: PAnsiDACChar; Params : PAnsiDACChar): longint; cdecl; + Tv3_Restore = function (AppName: PAnsiDACChar; LogFileName : PAnsiDACChar; Params : PAnsiDACChar): longint; cdecl; + Tpdmbvm_GetVersionAsInt = function():integer; cdecl; + Tpdmbvm_SetErrorCallBackProc = procedure(ProcAddr : pointer); cdecl; + Tpdmbvm_SetLogCallBackProc = procedure(ProcAddr : pointer); cdecl; + + TpdmvmParams = class + private + FParams : TStringList; + FArr : PAnsiDACChar; + + procedure ClearMem(); + public + constructor Create(); + destructor Destroy();override; + + procedure Add(aStr : string); + procedure Clear(); + + function GetPCharArray() : PAnsiDACChar; + end; + + + TLogEvent = procedure (Sender: TObject; const LogMessage: string) of object; + + TLibraryLoadEvent = procedure (Sender: TObject; var FileName: string) of object; + + EPSQLDumpException = class(Exception); + + TDumpRestoreSection = (drsPreData, drsData, drsPostData); + + TDumpRestoreSections = set of TDumpRestoreSection; + + TDumpOption = (doDataOnly, doIncludeBLOBs, doClean, doCreate, doInserts, + doColumnInserts, doIgnoreVersion, doOIDs, doNoOwner, + doSchemaOnly, doVerbose, doNoPrivileges, doDisableDollarQuoting, + doDisableTriggers, doUseSetSessionAuthorization, doNoTablespaces, + doQuoteAllIdentifiers, doNoSecurityLabels, doNoUnloggedTableData, + doSerializableDeferrable, doNoSynchronizedSnapshots, doIfExists, + doEnableRowSecurity, doStrictNames, doNoBlobs, doNoSync, + doNoSubscriptions, doNoPublications, doLoadViaPartitionRoot, + doNoComments); + + TDumpOptions = set of TDumpOption; + + TDumpStrOption = (dsoSchema, dsoSuperuser, dsoTable, dsoExcludeSchema, + dsoExcludeTable, dsoEncoding, dsoRole, dsoExcludeTableData, + dsoSnapshot); + + TDumpFormat = (dfPlain, dfTarArchive, dfCompressedArchive, dfDirectory); + + TCompressLevel = 0..9; + + TPSQLDump = class(TComponent) + private + FAbout : TPSQLDACAbout; + {$IFDEF M_DEBUG} + FParamStr : string; + {$ENDIF} + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; + FCompressLevel: TCompressLevel; + FDumpFormat : TDumpFormat; + FDumpOptions : TDumpOptions; + FDumpStrOptions : array[TDumpStrOption] of string; + FBeforeDump : TNotifyEvent; + FAfterDump : TNotifyEvent; + FRewriteFile: boolean; + FmiParams: TpdmvmParams; + FTableNames: TStrings; + FSchemaNames: TStrings; + FExcludeTables: TStrings; + FExcludeSchemas: TStrings; + FOnLog: TLogEvent; + FLockWaitTimeout: cardinal; + FOnLibraryLoad: TLibraryLoadEvent; + FJobs: cardinal; + FExcludeTablesData: TStrings; + FSections: TDumpRestoreSections; + procedure SetDatabase(const Value : TPSQLDatabase); + procedure SetCompressLevel(const Value: TCompressLevel); + function GetStrOptions(const Index: Integer): string; + procedure SetStrOptions(const Index: Integer; const Value: string); + procedure Dump(const TargetFile, LogFile: string); + procedure SetTableNames(const Value: TStrings); + procedure SetSchemaNames(const Value: TStrings); + procedure SetExcludeSchemas(const Value: TStrings); + procedure SetExcludeTables(const Value: TStrings); + procedure DoLog(const Value: string); + function GetVersionAsInt: integer; + function GetVersionAsStr: string; + procedure ReadTableName(Reader: TReader); //deal with old missing properties + procedure ReadSchemaName(Reader: TReader); //deal with old missing properties + procedure SetLockWaitTimeout(const Value: cardinal); + procedure SetDumpOptions(const Value: TDumpOptions); + procedure SetExcludeTablesData(const Value: TStrings); + protected + procedure CheckDependencies; + procedure DefineProperties(Filer: TFiler); override; + function GetParameters(OutputFileName: string): PAnsiDACChar; + procedure Notification( AComponent: TComponent; Operation: TOperation ); Override; + public + constructor Create(Owner : TComponent); override; + destructor Destroy; override; + procedure DumpToStream(Stream: TStream); overload; + procedure DumpToStream(Stream: TStream; Log: TStrings); overload; + procedure DumpToStream(Stream: TStream; LogFileName: string); overload; + procedure DumpToFile(const FileName: string; Log: TStrings); overload; + procedure DumpToFile(const FileName, LogFileName: string); overload; + property VersionAsInt: integer read GetVersionAsInt; + property VersionAsStr: string read GetVersionAsStr; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property CompressLevel: TCompressLevel read FCompressLevel write SetCompressLevel default 0; + property Database : TPSQLDatabase read FDatabase write SetDatabase; + property DumpFormat : TDumpFormat read FDumpFormat write FDumpFormat default dfPlain; + property Encoding: string index dsoEncoding read GetStrOptions write SetStrOptions; + property ExcludeSchemas: TStrings read FExcludeSchemas write SetExcludeSchemas; + property ExcludeTables: TStrings read FExcludeTables write SetExcludeTables; + property ExcludeTablesData: TStrings read FExcludeTablesData write SetExcludeTablesData; + property LockWaitTimeout: cardinal read FLockWaitTimeout write SetLockWaitTimeout default 0; + property Options : TDumpOptions read FDumpOptions write SetDumpOptions default []; + property RewriteFile: boolean read FRewriteFile write FRewriteFile default True; + property Role: string index dsoRole read GetStrOptions write SetStrOptions; + property SchemaNames: TStrings read FSchemaNames write SetSchemaNames; + property SuperUserName: string index dsoSuperUser read GetStrOptions write SetStrOptions; + property TableNames: TStrings read FTableNames write SetTableNames; + property Jobs: cardinal read FJobs write FJobs; + property Sections: TDumpRestoreSections read FSections write FSections; + property Snapshot: string index dsoSnapshot read GetStrOptions write SetStrOptions; + property AfterDump : TNotifyEvent read FAfterDump write FAfterDump; + property BeforeDump : TNotifyEvent read FBeforeDump write FBeforeDump; + property OnLog: TLogEvent read FOnLog write FOnLog; + property OnLibraryLoad: TLibraryLoadEvent read FOnLibraryLoad write FOnLibraryLoad; + end; + + +{TPSQLRestore stuff} + EPSQLRestoreException = class(Exception); + + TRestoreFormat = (rfAuto, rfTarArchive, rfCompressedArchive, rfDirectory); + + TRestoreOption = (roDataOnly, roClean, roCreate, roExitOnError, roIgnoreVersion, + roList, roNoOwner, roSchemaOnly, roVerbose, roNoPrivileges, + roDisableTriggers, roUseSetSessionAuthorization, roSingleTransaction, + roNoDataForFailedTables, roNoTablespaces, roNoSecurityLabels, roIfExists, + roEnableRowSecurity, roStrictNames, roNoSubscriptions, roNoPublications); + + TRestoreOptions = set of TRestoreOption; + + TRestoreStrOption = (rsoTable, rsoSuperUser, rsoDBName, + rsoFileName, rsoIndex, rsoListFile, rsoFunction, + rsoTrigger, rsoRole, rsoSchemaName, rsoExcludeSchema); + + TPSQLRestore = class(TComponent) + private + FAbout: TPSQLDACAbout; + {$IFDEF M_DEBUG} + FParamStr: string; + {$ENDIF} + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; + FRestoreFormat: TRestoreFormat; + FRestoreOptions: TRestoreOptions; + FRestoreStrOptions: array [TRestoreStrOption] of string; + FBeforeRestore: TNotifyEvent; + FAfterRestore: TNotifyEvent; + FmiParams: TpdmvmParams; + FOnLog: TLogEvent; + FJobs: cardinal; + FOnLibraryLoad: TLibraryLoadEvent; + FTableNames: TStrings; + FSections: TDumpRestoreSections; + FSchemaNames: TStrings; + FExcludeSchemas: TStrings; + procedure SetDatabase(const Value : TPSQLDatabase); + function GetStrOptions(const AnIndex: Integer): string; + procedure SetStrOptions(const AnIndex: Integer; const Value: string); + procedure Restore(const SourceFile, LogFile: string); + procedure DoLog(const Value: string); + function GetVersionAsInt: integer; + procedure SetJobs(const Value: cardinal); + procedure SetRestoreOptions(const Value: TRestoreOptions); + function GetVersionAsStr: string; + procedure SetTableNames(const Value: TStrings); + procedure ReadTableName(Reader: TReader); + procedure ReadSchemaName(Reader: TReader); + procedure SetSchemaNames(const Value: TStrings); + procedure SetExcludeSchemas(const Value: TStrings); + protected + procedure CheckDependencies; + procedure DefineProperties(Filer: TFiler); override; + function GetParameters(SourceFileName: string): PAnsiDACChar; + procedure Notification( AComponent: TComponent; Operation: TOperation ); Override; + public + constructor Create(Owner : TComponent); override; + destructor Destroy; override; + procedure RestoreFromFile(const FileName: string; Log: TStrings); overload; + procedure RestoreFromFile(const FileName, LogFileName: string); overload; + property VersionAsInt: integer read GetVersionAsInt; + property VersionAsStr: string read GetVersionAsStr; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property Database : TPSQLDatabase read FDatabase write SetDatabase; + property DBName: string index rsoDBName read GetStrOptions write SetStrOptions; + property FunctionDecl: string index rsoFunction read GetStrOptions write SetStrOptions; + property Index: string index rsoIndex read GetStrOptions write SetStrOptions; + property Jobs: cardinal read FJobs write SetJobs; + property ListFile: string index rsoListFile read GetStrOptions write SetStrOptions; + property Options : TRestoreOptions read FRestoreOptions write SetRestoreOptions; + property OutputFileName: string index rsoFileName read GetStrOptions write SetStrOptions; + property RestoreFormat : TRestoreFormat read FRestoreFormat write FRestoreFormat; + property Role: string index dsoRole read GetStrOptions write SetStrOptions; + property SuperUserName: string index rsoSuperUser read GetStrOptions write SetStrOptions; + property Trigger: string index rsoTrigger read GetStrOptions write SetStrOptions; + property TableNames: TStrings read FTableNames write SetTableNames; + property SchemaNames: TStrings read FSchemaNames write SetSchemaNames; + property ExcludeSchemas: TStrings read FExcludeSchemas write SetExcludeSchemas; + property Sections: TDumpRestoreSections read FSections write FSections; + property AfterRestore : TNotifyEvent read FAfterRestore write FAfterRestore; + property BeforeRestore : TNotifyEvent read FBeforeRestore write FBeforeRestore; + property OnLog: TLogEvent read FOnLog write FOnLog; + property OnLibraryLoad: TLibraryLoadEvent read FOnLibraryLoad write FOnLibraryLoad; + end; + +const + DumpRestoreCommandLineSectionParameters: array[TDumpRestoreSection] of string = ( + '--section=pre-data', //drsPreData + '--section=data', //drsData + '--section=post-data' //drsPostData + ); + + DumpCommandLineBoolParameters: array[TDumpOption] of string = ( + '--data-only', //doDataOnly + '--blobs', //doIncludeBLOBs + '--clean', //doClean + '--create', //doCreate + '--inserts', //doInserts + '--column-inserts', //doColumnInserts, aka '--attribute-inserts' + '--ignore-version', //doIgnoreVersion + '--oids', //doOIDs + '--no-owner', //doNoOwner + '--schema-only', //doSchemaOnly + '--verbose', //doVerbose, + '--no-privileges', //doNoPrivileges, aka '--no-acl' + '--disable-dollar-quoting', //doDisableDollarQuoting, + '--disable-triggers', //doDisableTriggers, + '--use-set-session-authorization', //doUseSetSessionAuthorization, + '--no-tablespaces', //doNoTablespaces + '--quote-all-identifiers', //doQuoteAllIdentifiers + '--no-security-labels', //doNoSecurityLabels + '--no-unlogged-table-data', //doNoUnloggedTableData + '--serializable-deferrable', //doSerializableDeferrable + '--no-synchronized-snapshots', //doNoSynchronizedSnapshots + '--if-exists', //doIfExists + '--enable-row-security', //doEnableRowSecurity + '--strict-names', //doStrictNames + '--no-blobs', //doNoBlobs + '--no-sync', //doNoSync + '--no-subscriptions', //doNoSubscriptions + '--no-publications', //doNoPublications + '--load-via-partition-root', //doLoadViaPartitionRoot + '--no-comments' //doNoComments + + + ); + + RestoreCommandLineBoolParameters: array[TRestoreOption] of string = ( + //--no-reconnect obsolete + '--data-only', //roDataOnly + '--clean', //roClean + '--create', //roCreate + '--exit-on-error', //roExitOnError + '--ignore-version', //roIgnoreVersion + '--list', //roList, aka '--no-acl' + '--no-owner', //roNoOwner + '--schema-only', //roSchemaOnly + '--verbose', //roVerbose, + '--no-privileges', //roNoPrivileges, + '--disable-triggers', //roDisableTriggers, + '--use-set-session-authorization', //roUseSetSessionAuthorization + '--single-transaction', //roSingleTransaction + '--no-data-for-failed-tables', //roNoDataForFailedTables + '--no-tablespaces', //roNoTablespaces + '--no-security-labels', //roNoSecurityLabels + '--if-exists', //roIfExists + '--enable-row-security', //roEnableRowSecurity + '--strict-names', //roStrictNames + '--no-subscriptions', //roNoSubscriptions + '--no-publications' //roNoPublications + ); + + DumpCommandLineStrParameters: array[TDumpStrOption] of string =( + '--schema=', //dsoSchema + '--superuser=', //dsoSuperuser + '--table=', //dsoTable + '--exclude-schema=', //dsoExcludeSchema + '--exclude-table=', //dsoExcludeTable + '--encoding=', //dsoEncoding + '--role=', //dsoRole + '--exclude-table-data=', //dsoExcludeTableData + '--snapshot=' //dsoSnapshot + ); + + RestoreCommandLineStrParameters: array[TRestoreStrOption] of string =( + '--table=', //rsoTable + '--superuser=', //rsoSuperuser + '--dbname=', //rsoDBName, + '--file=', //rsoFileName, + '--index=', //rsoIndex, + '--use-list=', //rsoListFile, + '--function=', //rsoFunction, + '--trigger=', //rsoTrigger + '--role=', //dsoRole + '--schema=', //rsoSchemaName + '--exclude-schema=' //rsoExcludeSchema + ); + + + DumpCommandLineFormatValues: array[TDumpFormat] of string = ( + '--format=p', //plain + '--format=t', //tar archive + '--format=c', //custom archive + '--format=d' //directory + ); + + RestoreCommandLineFormatValues: array[TRestoreFormat] of string = ( + '', //auto + '--format=t', //tar archive + '--format=c', //custom archive + '--format=d' //directory + ); + +function GetTempFileName: string; + +implementation + +uses PSQLAccess, +{$IFDEF MSWINDOWS} + Windows +{$ENDIF} +{$IFDEF POSIX} + Posix.SysTypes, Posix.Stdio, Posix.Stdlib +{$ENDIF}; + +var ProccessOwner: TComponent = nil; + +function IntVerToStr(VerInt: integer): string; +var Major, Minor, Revision: integer; +begin + Major := VerInt div 10000; + Minor := VerInt mod 10000 div 100; + Revision := VerInt mod 100; + Result := Format('%d.%d.%d', [Major, Minor, Revision]); +end; + +function StringToAnsiChar(CodePage: Cardinal; Src: {$IFNDEF NEXTGEN}WideString{$ELSE}String{$ENDIF}): PAnsiDACChar; +var + Len: integer; +begin + Result := nil; + if Src = EmptyStr then Exit; + Len := {$IFDEF DELPHI_16} + LocaleCharsFromUnicode + {$ELSE} + WideCharToMultiByte + {$ENDIF}(codePage, 0, PWideChar(Src), Length(Src), nil, 0, nil, nil); + if Len = 0 then Exit; + GetMem(Result, Len + 1); + if {$IFDEF DELPHI_16} + LocaleCharsFromUnicode + {$ELSE} + WideCharToMultiByte + {$ENDIF}(codePage, 0, PWideChar(Src), Length(Src), PAnsiDACChar(Result), Len, nil, nil) = 0 then + ReallocMem(Result, 0) + else + Result[Len] := #0; +end; + +function GetTempPath: string; +{$IFDEF MSWINDOWS} +var + Len: Integer; +begin + SetLastError(ERROR_SUCCESS); + + // get memory for the buffer retaining the temp path (plus null-termination) + SetLength(Result, MAX_PATH); + Len := Windows.GetTempPath(MAX_PATH, PChar(Result)); + if Len = 0 then + Result := ''; +end; +{$ENDIF} +{$IFDEF POSIX} +const + CEnvVars: array[0..2] of DACAString = ('TMPDIR', 'TMP', 'TEMP'); // Do not localize + CTmpDir = '/tmp'; // Do not localize + +var + LTempPathVar: PAnsiDACChar; + I: Integer; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + { Lookup env variables, in order: TMPDIR, TMP, TEMP } + for I := Low(CEnvVars) to High(CEnvVars) do + begin + LTempPathVar := getenv({$IFNDEF NEXTGEN} + PAnsiChar(CEnvVars[I]) + {$ELSE} + M.AsAnsi(CEnvVars[I]).ToPointer + {$ENDIF} + ); + + if (LTempPathVar <> nil) and (LTempPathVar^ <> #0) then + begin + { We have found our temporary path } + Break; + end; + end; + + { Get the UTF16 value out of the UTF8 one. The last resort is to fallback to /tmp } + if LTempPathVar <> nil then + Result := UTF8ToUnicodeString(LTempPathVar) + else + Result := CTmpDir; +end; +{$ENDIF} + +function GetTempFileName: string; +{$IFDEF MSWINDOWS} +var + TempPath: string; + ErrCode: UINT; +begin + TempPath := GetTempPath(); + SetLength(Result, MAX_PATH); + + SetLastError(ERROR_SUCCESS); + ErrCode := Windows.GetTempFileName(PChar(TempPath), 'tmp', 0, PChar(Result)); // DO NOT LOCALIZE + if ErrCode = 0 then + raise EInOutError.Create(SysErrorMessage(GetLastError)); + + SetLength(Result, StrLen(PChar(Result))); +end; +{$ENDIF} +{$IFDEF POSIX} +var + LTempPath: PAnsiDACChar; +begin + { Obtain a temporary file name } + LTempPath := tmpnam(nil); + + { Convert to UTF16 or leave blank on possible error } + if LTempPath <> nil then + Result := UTF8ToUnicodeString(LTempPath) + else + Result := ''; +end; +{$ENDIF} + +{ TpdmvmParams } +procedure TpdmvmParams.Add(aStr: string); +begin + FParams.Add(aStr); +end; + +procedure TpdmvmParams.Clear; +begin + FParams.Clear(); +end; + +procedure TpdmvmParams.ClearMem; +var + s : PAnsiDACChar; + p : PInteger; //32-bit pointer +begin + if FArr <> nil then + begin + p := Pointer(FArr); + repeat + s := PAnsiDACChar(p^); + if s <> nil then + FreeMem(s); + Inc(p); + until s = nil; + FreeMem(FArr); + FArr := nil; + end; +end; + +constructor TpdmvmParams.Create; +begin + FParams := TStringList.Create(); + FArr := nil; +end; + +destructor TpdmvmParams.Destroy; +begin + ClearMem(); + FParams.Free(); + + inherited; +end; + +function TpdmvmParams.GetPcharArray: PAnsiDACChar; +var + s : PAnsiDACChar; + p : PInteger; //32-bit pointer + i : integer; +begin + ClearMem(); + + GetMem(FArr, SizeOf(PAnsiDACChar) * (FParams.Count + 1)); + p := Pointer(FArr); + for i:=0 to FParams.Count - 1 do + begin + if FParams.Names[i] = '--file' then + S := StringToAnsiChar(CP_ACP, FParams[i]) + else + S := StringToAnsiChar(CP_UTF8, FParams[i]); + p^ := Integer(s); + Inc(p); + end; + p^ := 0; + Result := FArr; +end; + +{TPSQLDump} + +Constructor TPSQLDump.Create(Owner : TComponent); +var I: integer; +begin + inherited Create(Owner); + FDumpOptions := []; + {$IFNDEF NEXTGEN} + ZeroMemory(@FDumpStrOptions,sizeof(FDumpStrOptions)); + {$ELSE} + FillChar(FDumpStrOptions, sizeof(FDumpStrOptions), 0); + {$ENDIF} + FRewriteFile := True; + FTableNames := TStringList.Create; + FExcludeTables := TStringList.Create; + FExcludeTablesData := TStringList.Create; + FExcludeSchemas := TStringList.Create; + FSchemaNames := TStringList.Create; + FmiParams := TpdmvmParams.Create; + if (csDesigning in ComponentState) and Assigned(Owner) then + for I := Owner.ComponentCount - 1 downto 0 do + if Owner.Components[I] is TPSQLDatabase then + begin + Database := Owner.Components[I] as TPSQLDatabase; + Break; + end; +end; + +destructor TPSQLDump.Destroy; +begin + FmiParams.Free; + FTableNames.Free; + FSchemaNames.Free; + FExcludeTables.Free; + FExcludeTablesData.Free; + FExcludeSchemas.Free; + inherited Destroy; +end; + +Procedure TPSQLDump.Notification( AComponent: TComponent; Operation: TOperation ); +begin + Inherited Notification(AComponent, Operation); + if (Operation = opRemove) and (AComponent = FDatabase) then + FDatabase := nil; +end; + +procedure TPSQLDump.SetDatabase(const Value : TPSQLDatabase); +begin + if Value <> FDatabase then + FDatabase := Value; +end; + +procedure TPSQLDump.SetCompressLevel(const Value: TCompressLevel); +begin + FCompressLevel := Value; +end; + +procedure TPSQLDump.DumpToFile(const FileName: string; Log: TStrings); +var + tmpLogFile: string; +begin + tmpLogFile := GetTempFileName(); + if tmpLogFile = '' then + raise EPSQLDumpException.Create('Can''t create temporary log file'); + try + DumpToFile(FileName, tmpLogFile); + finally + if Assigned(Log) then + Log.LoadFromFile(tmpLogFile); + SysUtils.DeleteFile(tmpLogFile); + end; +end; + +procedure TPSQLDump.DumpToStream(Stream: TStream); +begin + DumpToStream(Stream,nil); +end; + +procedure TPSQLDump.CheckDependencies; +begin + if not Assigned(FDatabase) then + raise EDatabaseError.Create('Property Database not set!'); + if not FDatabase.Connected then + FDatabase.Connected := True; + + if (DumpFormat <> dfDirectory) and (FJobs > 1) then + raise EPSQLRestoreException.Create('Multiple jobs can be used only with directory output format'); +{$IFDEF MSWINDOWS} + if FJobs > MAXIMUM_WAIT_OBJECTS then + raise EPSQLRestoreException.CreateFmt('Maximum number of parallel jobs is %d',[MAXIMUM_WAIT_OBJECTS]); +{$ENDIF} + + if [doDataOnly, doSchemaOnly] * FDumpOptions = [doDataOnly, doSchemaOnly] then + raise EPSQLDumpException.Create('Options "Schema only" and "Data only" cannot be used together'); + if [doDataOnly, doClean] * FDumpOptions = [doDataOnly, doClean] then + raise EPSQLDumpException.Create('Options "Clean" and "Data only" cannot be used together'); + if (doIncludeBLOBs in FDumpOptions) and (FDumpStrOPtions[dsoTable] > '') then + raise EPSQLDumpException.Create('Large-object output not supported for a single table.'#13#10+ + 'Use a full dump instead'); + if (doIncludeBLOBs in FDumpOptions) and (FDumpStrOPtions[dsoSchema] > '') then + raise EPSQLDumpException.Create('Large-object output not supported for a single schema.'#13#10+ + 'Use a full dump instead'); + if [doInserts, doOids] * FDumpOPtions = [doInserts, doOids] then + raise EPSQLDumpException.Create('"Insert" and "OID" options cannot be used together.'#13#10+ + 'The INSERT command cannot set OIDs'); + if (doIncludeBLOBs in FDumpOptions) and (FDumpFormat = dfPlain) then + raise EPSQLDumpException.Create('Large-object output is not supported for plain-text dump files.'#13#10+ + 'Use a different output format.'); +end; + +function TPSQLDump.GetParameters(OutputFileName: string): PAnsiDACChar; +var I: TDumpOption; + J: TDumpStrOption; + DRS: TDumpRestoreSection; + k: integer; +begin + if not Assigned(FDatabase) then + raise EPSQLDumpException.Create('Database property not assigned!'); + + FmiParams.Clear; + + FmiParams.Add('--file=' + OutputFileName); + + for I := Low(TDumpOption) to High(TDumpOption) do + if I in FDumpOptions then + FmiParams.Add(DumpCommandLineBoolParameters[I]); + + for J := Low(TDumpStrOption) to High(TDumpStrOption) do + if FDumpStrOptions[J] > '' then + FmiParams.Add(DumpCommandLineStrParameters[J] + FDumpStrOptions[J]); + + for DRS := Low(TDumpRestoreSection) to High(TDumpRestoreSection) do + if DRS in FSections then + FmiParams.Add(DumpRestoreCommandLineSectionParameters[DRS]); + + for k := 0 to FSchemaNames.Count-1 do + FmiParams.Add(DumpCommandLineStrParameters[dsoSchema] + FSchemaNames[k]); + + for k := 0 to FExcludeSchemas.Count-1 do + FmiParams.Add(DumpCommandLineStrParameters[dsoExcludeSchema] + FExcludeSchemas[k]); + + for k := 0 to FTableNames.Count-1 do + FmiParams.Add(DumpCommandLineStrParameters[dsoTable] + FTableNames[k]); + + for k := 0 to FExcludeTables.Count-1 do + FmiParams.Add(DumpCommandLineStrParameters[dsoExcludeTable] + FExcludeTables[k]); + + for k := 0 to FExcludeTablesData.Count-1 do + FmiParams.Add(DumpCommandLineStrParameters[dsoExcludeTableData] + FExcludeTablesData[k]); + + FmiParams.Add(DumpCommandLineFormatValues[FDumpFormat]); + + if FDumpFormat in [dfCompressedArchive, dfPlain] then + FmiParams.Add(Format('--compress=%d', [FCompressLevel])); + + if FLockWaitTimeout > 0 then + FmiParams.Add(Format('--lock-wait-timeout=%u', [FLockWaitTimeout])); + + if FJobs > 1 then + FmiParams.Add(Format('--jobs=%u', [FJobs])); + + FmiParams.Add('--no-password'); //we will put it using environment variable + + with FDatabase do + begin + FmiParams.Add(Format('--username=%s',[UserName])); + FmiParams.Add(Format('--port=%d',[Port])); + FmiParams.Add(Format('--host=%s',[Host])); + FmiParams.Add(DatabaseName); + end; + + Result := FmiParams.GetPCharArray(); + + {$IFDEF M_DEBUG} + FParamStr := FmiParams.FParams.Commatext; + {$ENDIF} +end; + +function TPSQLDump.GetStrOptions(const Index: Integer): string; +begin + Result := FDumpStrOptions[TDumpStrOption(Index)]; +end; + +procedure TPSQLDump.SetStrOptions(const Index: Integer; + const Value: string); +begin + FDumpStrOptions[TDumpStrOption(Index)] := Value; +end; + +procedure TPSQLDump.DumpToFile(const FileName, LogFileName: string); +begin + CheckDependencies; + + if Assigned(FBeforeDump) then + FBeforeDump(Self); + + Dump(FileName, LogFileName); + + If Assigned(FAfterDump) then + FAfterDump(Self); +end; + +procedure TPSQLDump.DumpToStream(Stream: TStream; Log: TStrings); +var + tmpLogFile: string; +begin + tmpLogFile := GetTempFileName(); + if Assigned(Log) then + if tmpLogFile = '' then + raise EPSQLDumpException.Create('Can''t create temporary log file'); + try + DumpToStream(Stream,tmpLogFile); + finally + if Assigned(Log) then + begin + Log.LoadFromFile(tmpLogFile); + SysUtils.DeleteFile(tmpLogFile); + end; + end; +end; + +procedure TPSQLDump.DumpToStream(Stream: TStream; LogFileName: string); +var + tmpTargetFile: string; + FS: TFileStream; +begin + tmpTargetFile := GetTempFileName(); + if tmpTargetFile = '' then + raise EPSQLDumpException.Create('Can''t create temporary target file'); + try + DumpToFile(tmpTargetFile,LogFileName); + if Assigned(Stream) then + begin + FS := TFileStream.Create(tmpTargetFile,fmOpenRead); + try + Stream.CopyFrom(FS,FS.Size); + finally + FS.Free; + end; + end; + finally + SysUtils.DeleteFile(tmpTargetFile); + end; +end; + +procedure ErrorCallBackProc(Code: integer);cdecl; +begin + {This callback ALWAYS must raise an exception! + In any way dump or restore process will be aborted} + raise EPSQLDumpException.Create(Format('Error with code: %d', [Code])); +end; + +procedure LogCallBackProc(S: PAnsiDACChar);cdecl; +begin + if Assigned(ProccessOwner) then + if (ProccessOwner is TPSQLDump) then + (ProccessOwner as TPSQLDump).DoLog(TrimRight(UTF8ToString(S))) + else + if (ProccessOwner is TPSQLRestore) then + (ProccessOwner as TPSQLRestore).DoLog(TrimRight(UTF8ToString(S))); +end; + +{$IFDEF UNDER_DELPHI_6} +function DirectoryExists(const Name: string): Boolean; +var + Code: Integer; +begin + Code := GetFileAttributes(PChar(Name)); + Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); +end; +{$ENDIF} + +{$IFDEF DELPHI_5} +type UTF8String = AnsiString; +{$ENDIF} + +procedure UpdateEnv(PWD: {$IFNDEF NEXTGEN}UTF8String{$ELSE}String{$ENDIF}); +{$IFDEF MSWINDOWS} +type + tputenv = function(NameValue: PAnsiDACChar): integer; cdecl; +var + putenv: tputenv; +{$ENDIF} +{$IFDEF NEXTGEN} +var + M: TMarshaller; +{$ENDIF} +begin +{$IFDEF MSWINDOWS} + @putenv := GetProcAddress(GetModuleHandle('msvcrt'), '_putenv'); + if not Assigned(putenv) then + raise EPSQLDumpException.Create('Cannot obtain _putenv procedure entry point'); + if putenv(PAnsiChar('PGPASSWORD=' + PWD)) <> 0 then + raise EPSQLDumpException.Create('Cannot populate environment settings'); + if not SetEnvironmentVariableA('PGPASSWORD', PAnsiChar(PWD)) then + {$IFDEF DELPHI_5} + RaiseLastWin32Error(); + {$ELSE} + RaiseLastOSError(); + {$ENDIF} +{$ELSE POSIX} + SetEnv('PGPASSWORD', {$IFNDEF NEXTGEN}PAnsiChar(PWD){$ELSE}M.AsAnsi(PWD).ToPointer{$ENDIF}, 1); +{$ENDIF} +end; + +procedure TPSQLDump.Dump(const TargetFile, LogFile: string); +var + h : Cardinal; + Result: longint; + S: string; + + PLog: PAnsiDACChar; + Params: PAnsiDACChar; + + v3_dump: Tv3_dump; + pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; + pdmbvm_SetErrorCallBackProc : Tpdmbvm_SetErrorCallBackProc; + pdmbvm_SetLogCallBackProc : Tpdmbvm_SetLogCallBackProc; + + LibName: string; +begin + if (FDumpFormat = dfDirectory) then + begin + if DirectoryExists(TargetFile) then + raise EPSQLDumpException.Create('Cannot create dump. Target directory exists: ' + TargetFile); + end + else + if FileExists(TargetFile) and not FRewriteFile then + raise EPSQLDumpException.Create('Cannot create dump. Target file exists: ' + TargetFile); + + LibName := 'pg_dump.dll'; + if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); + h := SafeLoadLibrary(PChar(LibName)); + {$IFDEF M_DEBUG} + LogDebugMessage('DUMPLIB', GetModuleName(h)); + {$ENDIF} + try + @v3_dump := GetProcAddress(h, PChar('v3_dump')); + if not assigned(@v3_dump) then + raise EPSQLDumpException.Create('Can''t load pg_dump.dll'); + + @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); + + {$IFDEF M_DEBUG} + LogDebugMessage('DUMPVER', IntToStr(pdmbvm_GetVersionAsInt())); + {$ENDIF} + + @pdmbvm_SetErrorCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetErrorCallBackProc')); + @pdmbvm_SetLogCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetLogCallBackProc')); + + pdmbvm_SetErrorCallBackProc(@ErrorCallBackProc); + if Assigned(FOnLog) then + begin + ProccessOwner := Self; + pdmbvm_SetLogCallBackProc(@LogCallBackProc); + end; + PLog := StringToAnsiChar(CP_ACP, LogFile); + UpdateEnv({$IFDEF NEXTGEN}String{$ENDIF}(UTF8Encode(FDatabase.UserPassword))); + Params := GetParameters(TargetFile); + {$IFDEF M_DEBUG} + LogDebugMessage('PARAMSTR', FParamStr); + {$ENDIF} + Result := v3_dump(PAnsiDACChar(UTF8Encode(ParamStr(0))), PLog, Params); + + case Result of + 0: S := ''; + 1: S := 'Common dump error'; + 2: S := 'Connection error (wrong username, password, host etc.)'; + 3: S := 'File IO error'; + else + S := 'Uknown dump error'; + end; + if S > '' then + raise EPSQLDumpException.Create(S + #13#10 + Format('Error Code: %d', [Result])); + + finally + ProccessOwner := nil; + ReallocMem(PLog, 0); + FreeLibrary(h); + end; +end; + +procedure TPSQLDump.SetTableNames(const Value: TStrings); +begin + FTableNames.Assign(Value); +end; + +procedure TPSQLDump.SetSchemaNames(const Value: TStrings); +begin + FSchemaNames.Assign(Value); +end; + +procedure TPSQLDump.SetExcludeSchemas(const Value: TStrings); +begin + FExcludeSchemas.Assign(Value); +end; + +procedure TPSQLDump.SetExcludeTables(const Value: TStrings); +begin + FExcludeTables.Assign(Value); +end; + +procedure TPSQLDump.SetExcludeTablesData(const Value: TStrings); +begin + FExcludeTablesData.Assign(Value); +end; + +procedure TPSQLDump.DoLog(const Value: string); +begin + if Assigned(FOnLog) then + FOnLog(Self, Value); +end; + +function TPSQLDump.GetVersionAsInt: integer; +var + h : Cardinal; + LibName: string; + pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; +begin + Result := 0; + LibName := 'pg_dump.dll'; + if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); + h := LoadLibrary(PChar(LibName)); + try + @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); + if Assigned(pdmbvm_GetVersionAsInt) then + Result := pdmbvm_GetVersionAsInt(); + finally + FreeLibrary(H); + end; +end; + +function TPSQLDump.GetVersionAsStr: string; +begin + Result := IntVerToStr(GetVersionAsInt()); +end; + +procedure TPSQLDump.DefineProperties(Filer: TFiler); +begin + inherited; + Filer.DefineProperty('TableName', ReadTableName, nil, False); + Filer.DefineProperty('SchemaName', ReadSchemaName, nil, False); +end; + +procedure TPSQLDump.ReadSchemaName(Reader: TReader); +var S: string; +begin + S := Reader.ReadString; + if S > '' then FSchemaNames.Append(S); +end; + +procedure TPSQLDump.ReadTableName(Reader: TReader); +var S: string; +begin + S := Reader.ReadString; + if S > '' then FTableNames.Append(S); +end; + +procedure TPSQLDump.SetLockWaitTimeout(const Value: cardinal); +begin + FLockWaitTimeout := Value; +end; + +procedure TPSQLDump.SetDumpOptions(const Value: TDumpOptions); +begin + FDumpOptions := Value; + // since we allow use of custom lib this is not correct anymore + // Exclude(FDumpOptions, doIgnoreVersion); //deprecated and to be removed +end; + +{TPSQLRestore} + +constructor TPSQLRestore.Create(Owner: TComponent); +var I: integer; +begin + inherited; + FRestoreOptions := []; + FmiParams := TpdmvmParams.Create; + FRestoreFormat := rfAuto; + FTableNames := TStringList.Create; + FSchemaNames := TStringList.Create; + FExcludeSchemas := TStringList.Create; + {$IFNDEF NEXTGEN} + ZeroMemory(@FRestoreStrOptions, SizeOf(FRestoreStrOptions)); + {$ELSE} + FillChar(FRestoreStrOptions, SizeOf(FRestoreStrOptions), 0); + {$ENDIF} + if (csDesigning in ComponentState) and Assigned(Owner) then + for I := Owner.ComponentCount - 1 downto 0 do + if Owner.Components[I] is TPSQLDatabase then + begin + Database := Owner.Components[I] as TPSQLDatabase; + Break; + end; +end; + +procedure TPSQLRestore.DefineProperties(Filer: TFiler); +begin + inherited; + Filer.DefineProperty('TableName', ReadTableName, nil, False); + Filer.DefineProperty('SchemaName', ReadSchemaName, nil, False); +end; + +destructor TPSQLRestore.Destroy; +begin + FmiParams.Free; + FTableNames.Free; + FSchemaNames.Free; + FExcludeSchemas.Free; + inherited; +end; + +procedure TPSQLRestore.RestoreFromFile(const FileName: string; Log: TStrings); +var tmpLogFile: string; +begin + tmpLogFile := GetTempFileName(); + if tmpLogFile = '' then + raise EPSQLRestoreException.Create('Can''t create temporary log file'); + try + RestoreFromFile(FileName,tmpLogFile); + finally + If Assigned(Log) then + Log.LoadFromFile(tmpLogFile); + SysUtils.DeleteFile(tmpLogFile); + end; +end; + +procedure TPSQLRestore.RestoreFromFile(const FileName, LogFileName: string); +begin + CheckDependencies; + + if Assigned(FBeforeRestore) then + FBeforeRestore(Self); + + Restore(FileName, LogFileName); + + if Assigned(FAfterRestore) then + FAfterRestore(Self); +end; + +function TPSQLRestore.GetParameters(SourceFileName: string): PAnsiDACChar; +var I: TRestoreOption; + J: TRestoreStrOption; + K: Integer; + DRS: TDumpRestoreSection; +begin + if not Assigned(FDatabase) then + raise EPSQLRestoreException.Create('Database property not assigned!'); + + FmiParams.Clear; + + for I := Low(TRestoreOption) to Pred(High(TRestoreOption)) do + if I in FRestoreOptions then + FmiParams.Add(RestoreCommandLineBoolParameters[I]); + + for J := Low(TRestoreStrOption) to High(TRestoreStrOption) do + if (FRestoreStrOptions[J] > '') then + FmiParams.Add(RestoreCommandLineStrParameters[J] + FRestoreStrOPtions[J]); + + for DRS := Low(TDumpRestoreSection) to High(TDumpRestoreSection) do + if DRS in FSections then + FmiParams.Add(DumpRestoreCommandLineSectionParameters[DRS]); + + if FRestoreFormat <> rfAuto then + FmiParams.Add(RestoreCommandLineFormatValues[FRestoreFormat]); + + for k := 0 to FSchemaNames.Count-1 do + FmiParams.Add(RestoreCommandLineStrParameters[rsoSchemaName] + FSchemaNames[k]); + + for k := 0 to FExcludeSchemas.Count-1 do + FmiParams.Add(RestoreCommandLineStrParameters[rsoExcludeSchema] + FExcludeSchemas[k]); + + for k := 0 to FTableNames.Count-1 do + FmiParams.Add(RestoreCommandLineStrParameters[rsoTable] + FTableNames[k]); + + if FJobs > 1 then + FmiParams.Add(Format('--jobs=%u', [FJobs])); + + FmiParams.Add('--no-password'); //we will put it using environment variable + + with FDatabase do + begin + FmiParams.Add(Format('--username=%s',[UserName])); + FmiParams.Add(Format('--port=%d',[Port])); + FmiParams.Add(Format('--host=%s',[Host])); + end; + + FmiParams.Add(SourceFileName); + + Result := FmiParams.GetPCharArray(); + +{$IFDEF M_DEBUG} + FParamStr := FmiParams.FParams.Commatext; +{$ENDIF} +end; + +function TPSQLRestore.GetStrOptions(const AnIndex: Integer): string; +begin + Result := FRestoreStrOptions[TRestoreStrOption(AnIndex)]; +end; + +procedure TPSQLRestore.Notification(AComponent: TComponent; + Operation: TOperation); +begin + Inherited Notification(AComponent, Operation); + if (Operation = opRemove) and (AComponent = FDatabase) then + FDatabase := nil; +end; + +procedure TPSQLRestore.SetDatabase(const Value : TPSQLDatabase); +begin + if Value <> FDatabase then + FDatabase := Value; +end; + +procedure TPSQLRestore.SetExcludeSchemas(const Value: TStrings); +begin + FExcludeSchemas.Assign(Value); +end; + +procedure TPSQLRestore.SetSchemaNames(const Value: TStrings); +begin + FSchemaNames.Assign(Value); +end; + +procedure TPSQLRestore.SetStrOptions(const AnIndex: Integer; + const Value: string); +begin + FRestoreStrOptions[TRestoreStrOption(AnIndex)] := Value; +end; + +procedure TPSQLRestore.SetTableNames(const Value: TStrings); +begin + FTableNames.Assign(Value); +end; + +procedure TPSQLRestore.CheckDependencies; +var CheckDB: TPSQLDatabase; +begin + if not ((FRestoreStrOptions[rsoFileName] > '') xor (FRestoreStrOptions[rsoDBName] > '')) then + raise EPSQLRestoreException.Create('Database or file output should be specified, but not both'); + + + if (roSingleTransaction in FRestoreOptions) and (FJobs > 1) then + raise EPSQLRestoreException.Create('Multiple jobs cannot be used within the single transaction'); + + {$IFDEF MSWINDOWS} + if FJobs > MAXIMUM_WAIT_OBJECTS then + raise EPSQLRestoreException.CreateFmt('Maximum number of parallel jobs is %d',[MAXIMUM_WAIT_OBJECTS]); + {$ENDIF} + + if (FRestoreStrOptions[rsoFileName] = '') + and (FRestoreStrOptions[rsoDBName] = '') + then + Raise EPSQLRestoreException.Create('At least database or file output must be specified'); + + if FRestoreStrOptions[rsoDBName] > '' then + begin + CheckDB := TPSQLDatabase.Create(nil); + try + CheckDB.DatabaseName := FRestoreStrOptions[rsoDBName]; + CheckDB.Host := FDatabase.Host; + CheckDB.Port := FDatabase.Port; + CheckDB.UserName := FDatabase.UserName; + CheckDB.UserPassword := FDatabase.UserPassword; + CheckDB.Connected := True; + finally + CheckDB.Connected := False; + CheckDB.Free; + end; + end; +end; + +procedure TPSQLRestore.ReadSchemaName(Reader: TReader); +var S: string; +begin + S := Reader.ReadString; + if S > '' then FSchemaNames.Append(S); +end; + +procedure TPSQLRestore.ReadTableName(Reader: TReader); +var S: string; +begin + S := Reader.ReadString; + if S > '' then FTableNames.Append(S); +end; + +procedure TPSQLRestore.Restore(const SourceFile, LogFile: string); +var + h : Cardinal; + Result: longint; + S: string; + + v3_restore: Tv3_Restore; + pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; + pdmbvm_SetErrorCallBackProc : Tpdmbvm_SetErrorCallBackProc; + pdmbvm_SetLogCallBackProc : Tpdmbvm_SetLogCallBackProc; + + PLog: PAnsiDACChar; + LibName: string; + Params: PAnsiDACChar; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} +begin + S := ''; + LibName := 'pg_restore.dll'; + if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); + h := SafeLoadLibrary(PChar(LibName)); + {$IFDEF M_DEBUG} + LogDebugMessage('RESTLIB', GetModuleName(h)); + {$ENDIF} + try + @v3_restore := GetProcAddress(h, PChar('v3_restore')); + if not assigned(@v3_restore) then + raise EPSQLRestoreException.Create('Can''t load pg_restore.dll'); + + @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); + + {$IFDEF M_DEBUG} + LogDebugMessage('RESTVER', IntToStr(pdmbvm_GetVersionAsInt())); + {$ENDIF} + + @pdmbvm_SetErrorCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetErrorCallBackProc')); + @pdmbvm_SetLogCallBackProc := GetProcAddress(h, PChar('pdmbvm_SetLogCallBackProc')); + + pdmbvm_SetErrorCallBackProc(@ErrorCallBackProc); + if Assigned(FOnLog) then + begin + ProccessOwner := Self; + pdmbvm_SetLogCallBackProc(@LogCallBackProc); + end; + if LogFile > '' then + {$IFNDEF NEXTGEN} + PLog := PAnsiDACChar(UTF8Encode(LogFile)) + {$ELSE} + PLog := PAnsiDACChar(M.AsAnsi(LogFile)) + {$ENDIF} + else + PLog := nil; + + UpdateEnv({$IFDEF NEXTGEN}string{$ENDIF}(UTF8Encode(FDatabase.UserPassword))); + Params := GetParameters(SourceFile); + + {$IFDEF M_DEBUG} + LogDebugMessage('PARAMSTR', FParamStr); + {$ENDIF} + + Result := v3_restore(PAnsiDACChar(UTF8Encode(ParamStr(0))), PLog, Params); + + case Result of + 0: ;// - OK + 1: if roExitOnError in Options then S := 'Common pg_restore error'; + 3: S := 'Output file error.'; //stdout operation + 4: S := 'Error output file error.'; //stderr + else + S := 'Unknown restore error'; + end; + + if S > '' then + raise EPSQLRestoreException.Create(S + #13#10 + Format('Error Code: %d', [Result])); + + finally + FreeLibrary(h); + end; +end; + +procedure TPSQLRestore.DoLog(const Value: string); +begin + if Assigned(FOnLog) then + FOnLog(Self, Value); +end; + +function TPSQLRestore.GetVersionAsInt: integer; +var + h : Cardinal; + LibName: string; + pdmbvm_GetVersionAsInt : Tpdmbvm_GetVersionAsInt; +begin + Result := 0; + LibName := 'pg_restore.dll'; + if Assigned(FOnLibraryLoad) then FOnLibraryLoad(Self, LibName); + h := LoadLibrary(PChar(LibName)); + try + @pdmbvm_GetVersionAsInt := GetProcAddress(h, PChar('pdmbvm_GetVersionAsInt')); + if Assigned(pdmbvm_GetVersionAsInt) then + Result := pdmbvm_GetVersionAsInt(); + finally + FreeLibrary(H); + end; +end; +function TPSQLRestore.GetVersionAsStr: string; +begin + Result := IntVerToStr(GetVersionAsInt()); +end; + +procedure TPSQLRestore.SetJobs(const Value: cardinal); +begin + FJobs := Value; +end; + +procedure TPSQLRestore.SetRestoreOptions(const Value: TRestoreOptions); +begin + FRestoreOptions := Value; + //since we allow use of custom lib this is not correct anymore + //Exclude(FRestoreOptions, roIgnoreVersion); +end; + +end. diff --git a/PSQLExtMask.pas b/Source/PSQLExtMask.pas similarity index 95% rename from PSQLExtMask.pas rename to Source/PSQLExtMask.pas index 71daaf0..1d845f1 100644 --- a/PSQLExtMask.pas +++ b/Source/PSQLExtMask.pas @@ -1,497 +1,497 @@ -{$I pSQLDAC.inc} - -unit PSQLExtMask; - -{SVN revision: $Id$} - -{$T-} - -interface - -uses SysUtils, PSQLTypes; - -type - EExtMaskException = class(Exception); - - TExtMask = class - private - FCaseSensitive: boolean; - FMask: Pointer; - FSize: Integer; - public - constructor Create(const MaskValue: string; const CaseSensitive: boolean = False; - const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'); - destructor Destroy; override; - function Matches(const AString: string): Boolean; - end; - - -function MatchesMask(const AString, Mask: string; const CaseSensitive: boolean = False; - const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'): Boolean; - -function EscapeMask(const AMask, EscapeChars: string): string; - -implementation - -const - MaxCards = 30; - -resourcestring - SInvalidMask = '''%s'' is an invalid mask at (%d)'; - -type - PMaskSet = ^TMaskSet; - TMaskSet = set of AnsiDACByteChar; - TMaskStates = (msLiteral, msAny, msSet, msMBCSLiteral); - TMaskState = record - SkipTo: Boolean; - case State: TMaskStates of - msLiteral: (Literal: AnsiDACByteChar); - msAny: (); - msSet: ( - Negate: Boolean; - CharSet: PMaskSet); - msMBCSLiteral: (LeadByte, TrailByte: AnsiDACByteChar); - end; - PMaskStateArray = ^TMaskStateArray; - TMaskStateArray = array[0..128] of TMaskState; - -function InitMaskStates(const Mask: string; - var MaskStates: array of TMaskState; - const MatchAnyChar: AnsiDACChar = '*'; - const MatchSingleChar: AnsiDACChar = '?'): Integer; -var - I: Integer; - SkipTo: Boolean; - Literal: AnsiDACByteChar; - LeadByte, TrailByte: AnsiDACByteChar; - P: PAnsiDACBytesChar; - Negate: Boolean; - CharSet: TMaskSet; - Cards: Integer; - - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} - - procedure InvalidMask; - begin - raise EExtMaskException.CreateResFmt(@SInvalidMask, [Mask, - P - PChar(Mask) + 1]); - end; - - procedure Reset; - begin - SkipTo := False; - Negate := False; - CharSet := []; - end; - - procedure WriteScan(MaskState: TMaskStates); - begin - if I <= High(MaskStates) then - begin - if SkipTo then - begin - Inc(Cards); - if Cards > MaxCards then InvalidMask; - end; - MaskStates[I].SkipTo := SkipTo; - MaskStates[I].State := MaskState; - case MaskState of - msLiteral: MaskStates[I].Literal := Literal; - msSet: - begin - MaskStates[I].Negate := Negate; - New(MaskStates[I].CharSet); - MaskStates[I].CharSet^ := CharSet; - end; - msMBCSLiteral: - begin - MaskStates[I].LeadByte := LeadByte; - MaskStates[I].TrailByte := TrailByte; - end; - end; - end; - Inc(I); - Reset; - end; - - {$IFNDEF NEXTGEN} - procedure ScanSet; - var - LastChar: AnsiChar; - C: AnsiChar; - begin - Inc(P); - if P^ = '!' then - begin - Negate := True; - Inc(P); - end; - LastChar := #0; - while not (P^ in [#0, ']']) do - begin - // MBCS characters not supported in msSet! - if P^ in LeadBytes then - Inc(P) - else - case P^ of - '-': - if LastChar = #0 then InvalidMask - else - begin - Inc(P); - for C := LastChar to (P^) do Include(CharSet, C); - end; - else - LastChar := (P^); - Include(CharSet, LastChar); - end; - Inc(P); - end; - if (P^ <> ']') or (CharSet = []) then InvalidMask; - WriteScan(msSet); - end; - {$ELSE} - procedure ScanSet; - var - LastChar: Byte; - C: Byte; - begin - Inc(P); - if P^ = ord('!') then - begin - Negate := True; - Inc(P); - end; - LastChar := 0; - while not (P^ in [0, ord(']')]) do - begin - // MBCS characters not supported in msSet! - if P^ in LeadBytes then - Inc(P) - else - case P^ of - ord('-'): - if LastChar = 0 then InvalidMask - else - begin - Inc(P); - for C := LastChar to (P^) do - Include(CharSet, C); - end; - else - LastChar := (P^); - Include(CharSet, LastChar); - end; - Inc(P); - end; - if (P^ <> ord(']')) or (CharSet = []) then InvalidMask; - WriteScan(msSet); - end; - {$ENDIF} - -begin - {$IFNDEF NEXTGEN} - P := PAnsiChar(AnsiString(Mask)); - I := 0; - Cards := 0; - Reset; - while P^ <> #0 do - begin - if P^ = MatchAnyChar then - SkipTo := True - else - if P^ = MatchSingleChar then - begin - if not SkipTo then WriteScan(msAny); - end - else - case P^ of - '[': ScanSet; - '\': begin - Inc(P); - If P^ <> #0 then - begin - Literal := P^; - WriteScan(msLiteral); - end; - end; - else - if P^ in LeadBytes then - begin - LeadByte := P^; - Inc(P); - TrailByte := P^; - WriteScan(msMBCSLiteral); - end - else - begin - Literal := P^; - WriteScan(msLiteral); - end; - end; - If P^ <> #0 then Inc(P); - end; - Literal := #0; - WriteScan(msLiteral); - Result := I; - {$ELSE} - P := M.AsAnsi(Mask).ToPointer; - I := 0; - Cards := 0; - Reset; - while P^ <> 0 do - begin - if P^ = ord(MatchAnyChar) then - SkipTo := True - else - if P^ = ord(MatchSingleChar) then - begin - if not SkipTo then WriteScan(msAny); - end - else - case P^ of - ord('['): ScanSet; - ord('\'): begin - Inc(P); - If P^ <> 0 then - begin - Literal := P^; - WriteScan(msLiteral); - end; - end; - else - if P^ in LeadBytes then - begin - LeadByte := P^; - Inc(P); - TrailByte := P^; - WriteScan(msMBCSLiteral); - end - else - begin - Literal := P^; - WriteScan(msLiteral); - end; - end; - If P^ <> 0 then Inc(P); - end; - Literal := 0; - WriteScan(msLiteral); - Result := I; - {$ENDIF} -end; - -function MatchesMaskStates(const Filename: DACAString; - const MaskStates: array of TMaskState): Boolean; -type - TStackRec = record - sP: PAnsiDACChar; - sI: Integer; - end; -var - T: Integer; - S: array[0..MaxCards - 1] of TStackRec; - I: Integer; - P: PAnsiDACChar; - {$IFDEF NEXTGEN} - M: TMarshaller; - {$ENDIF} - - procedure Push(P: PAnsiDACChar; I: Integer); - begin - with S[T] do - begin - sP := P; - sI := I; - end; - Inc(T); - end; - - function Pop(var P: PAnsiDACChar; var I: Integer): Boolean; - begin - if T = 0 then - Result := False - else - begin - Dec(T); - with S[T] do - begin - P := sP; - I := sI; - end; - Result := True; - end; - end; - - {$IFNDEF NEXTGEN} - function Matches(P: PAnsiChar; Start: Integer): Boolean; - var - I: Integer; - begin - Result := False; - for I := Start to High(MaskStates) do - with MaskStates[I] do - begin - if SkipTo then - begin - case State of - msLiteral: - while (P^ <> #0) and (P^ <> Literal) do Inc(P); - msSet: - while (P^ <> #0) and not (Negate xor (P^ in CharSet^)) do Inc(P); - msMBCSLiteral: - while (P^ <> #0) do - begin - if (P^ <> LeadByte) then Inc(P, 2) - else - begin - Inc(P); - if (P^ = TrailByte) then Break; - Inc(P); - end; - end; - end; - if P^ <> #0 then Push(@P[1], I); - end; - case State of - msLiteral: if P^ <> Literal then Exit; - msSet: if not (Negate xor (P^ in CharSet^)) then Exit; - msMBCSLiteral: - begin - if P^ <> LeadByte then Exit; - Inc(P); - if P^ <> TrailByte then Exit; - end; - end; - Inc(P); - end; - Result := True; - end; - {$ELSE} - function Matches(P: PAnsiDACBytesChar; Start: Integer): Boolean; - var - I: Integer; - begin - Result := False; - for I := Start to High(MaskStates) do - with MaskStates[I] do - begin - if SkipTo then - begin - case State of - msLiteral: - while (P^ <> 0) and (P^ <> Literal) do Inc(P); - msSet: - while (P^ <> 0) and not (Negate xor (P^ in CharSet^)) do Inc(P); - msMBCSLiteral: - while (P^ <> 0) do - begin - if (P^ <> LeadByte) then Inc(P, 2) - else - begin - Inc(P); - if (P^ = TrailByte) then Break; - Inc(P); - end; - end; - end; - if P^ <> 0 then Push(@P[1], I); - end; - case State of - msLiteral: if P^ <> Literal then Exit; - msSet: if not (Negate xor (P^ in CharSet^)) then Exit; - msMBCSLiteral: - begin - if P^ <> LeadByte then Exit; - Inc(P); - if P^ <> TrailByte then Exit; - end; - end; - Inc(P); - end; - Result := True; - end; - {$ENDIF} - -begin - Result := True; - T := 0; - P := {$IFNDEF NEXTGEN}PAnsiChar(Filename){$ELSE}M.AsAnsi(Filename).ToPointer{$ENDIF}; - I := Low(MaskStates); - repeat - if Matches(PAnsiDACBytesChar(P), I) then Exit; - until not Pop(P, I); - Result := False; -end; - -procedure DoneMaskStates(var MaskStates: array of TMaskState); -var - I: Integer; -begin - for I := Low(MaskStates) to High(MaskStates) do - if MaskStates[I].State = msSet then Dispose(MaskStates[I].CharSet); -end; - -{ TExtMask } - -constructor TExtMask.Create(const MaskValue: string; const CaseSensitive: boolean = False; - const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'); -var - A: array[0..0] of TMaskState; - S: string; -begin - FCaseSensitive := CaseSensitive; - If not FCaseSensitive then S := UpperCase(MaskValue) else S := MaskValue; - FSize := InitMaskStates(S, A, MatchAnyChar, MatchSingleChar); - FMask := AllocMem(FSize * SizeOf(TMaskState)); - InitMaskStates(S, Slice(PMaskStateArray(FMask)^, FSize), MatchAnyChar, MatchSingleChar); -end; - -destructor TExtMask.Destroy; -begin - if FMask <> nil then - begin - DoneMaskStates(Slice(PMaskStateArray(FMask)^, FSize)); - FreeMem(FMask, FSize * SizeOf(TMaskState)); - end; - inherited; -end; - -function TExtMask.Matches(const AString: string): Boolean; -var S: string; -begin - if not FCaseSensitive then S := UpperCase(AString) else S := AString; - Result := MatchesMaskStates(DACAString(S), Slice(PMaskStateArray(FMask)^, FSize)); -end; - -function MatchesMask(const AString, Mask: string; const CaseSensitive: boolean = False; - const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'): Boolean; -var - CMask: TExtMask; -begin - CMask := TExtMask.Create(Mask,CaseSensitive, MatchAnyChar, MatchSingleChar); - try - Result := CMask.Matches(AString); - finally - CMask.Free; - end; -end; - -function EscapeMask(const AMask, EscapeChars: string): string; -var - I, J: Integer; -begin - Result := AMask; - for I := Length(Result) downto 1 do - for J := Length(EscapeChars) downto 1 do - if Result[I] = EscapeChars[J] then - begin - Insert('\', Result, I); - Break; - end; -end; - -end. +{$I pSQLDAC.inc} + +unit PSQLExtMask; + +{SVN revision: $Id$} + +{$T-} + +interface + +uses SysUtils, PSQLTypes; + +type + EExtMaskException = class(Exception); + + TExtMask = class + private + FCaseSensitive: boolean; + FMask: Pointer; + FSize: Integer; + public + constructor Create(const MaskValue: string; const CaseSensitive: boolean = False; + const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'); + destructor Destroy; override; + function Matches(const AString: string): Boolean; + end; + + +function MatchesMask(const AString, Mask: string; const CaseSensitive: boolean = False; + const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'): Boolean; + +function EscapeMask(const AMask, EscapeChars: string): string; + +implementation + +const + MaxCards = 30; + +resourcestring + SInvalidMask = '''%s'' is an invalid mask at (%d)'; + +type + PMaskSet = ^TMaskSet; + TMaskSet = set of AnsiDACByteChar; + TMaskStates = (msLiteral, msAny, msSet, msMBCSLiteral); + TMaskState = record + SkipTo: Boolean; + case State: TMaskStates of + msLiteral: (Literal: AnsiDACByteChar); + msAny: (); + msSet: ( + Negate: Boolean; + CharSet: PMaskSet); + msMBCSLiteral: (LeadByte, TrailByte: AnsiDACByteChar); + end; + PMaskStateArray = ^TMaskStateArray; + TMaskStateArray = array[0..128] of TMaskState; + +function InitMaskStates(const Mask: string; + var MaskStates: array of TMaskState; + const MatchAnyChar: AnsiDACChar = '*'; + const MatchSingleChar: AnsiDACChar = '?'): Integer; +var + I: Integer; + SkipTo: Boolean; + Literal: AnsiDACByteChar; + LeadByte, TrailByte: AnsiDACByteChar; + P: PAnsiDACBytesChar; + Negate: Boolean; + CharSet: TMaskSet; + Cards: Integer; + + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} + + procedure InvalidMask; + begin + raise EExtMaskException.CreateResFmt(@SInvalidMask, [Mask, + P - PChar(Mask) + 1]); + end; + + procedure Reset; + begin + SkipTo := False; + Negate := False; + CharSet := []; + end; + + procedure WriteScan(MaskState: TMaskStates); + begin + if I <= High(MaskStates) then + begin + if SkipTo then + begin + Inc(Cards); + if Cards > MaxCards then InvalidMask; + end; + MaskStates[I].SkipTo := SkipTo; + MaskStates[I].State := MaskState; + case MaskState of + msLiteral: MaskStates[I].Literal := Literal; + msSet: + begin + MaskStates[I].Negate := Negate; + New(MaskStates[I].CharSet); + MaskStates[I].CharSet^ := CharSet; + end; + msMBCSLiteral: + begin + MaskStates[I].LeadByte := LeadByte; + MaskStates[I].TrailByte := TrailByte; + end; + end; + end; + Inc(I); + Reset; + end; + + {$IFNDEF NEXTGEN} + procedure ScanSet; + var + LastChar: AnsiChar; + C: AnsiChar; + begin + Inc(P); + if P^ = '!' then + begin + Negate := True; + Inc(P); + end; + LastChar := #0; + while not (P^ in [#0, ']']) do + begin + // MBCS characters not supported in msSet! + if P^ in LeadBytes then + Inc(P) + else + case P^ of + '-': + if LastChar = #0 then InvalidMask + else + begin + Inc(P); + for C := LastChar to (P^) do Include(CharSet, C); + end; + else + LastChar := (P^); + Include(CharSet, LastChar); + end; + Inc(P); + end; + if (P^ <> ']') or (CharSet = []) then InvalidMask; + WriteScan(msSet); + end; + {$ELSE} + procedure ScanSet; + var + LastChar: Byte; + C: Byte; + begin + Inc(P); + if P^ = ord('!') then + begin + Negate := True; + Inc(P); + end; + LastChar := 0; + while not (P^ in [0, ord(']')]) do + begin + // MBCS characters not supported in msSet! + if P^ in LeadBytes then + Inc(P) + else + case P^ of + ord('-'): + if LastChar = 0 then InvalidMask + else + begin + Inc(P); + for C := LastChar to (P^) do + Include(CharSet, C); + end; + else + LastChar := (P^); + Include(CharSet, LastChar); + end; + Inc(P); + end; + if (P^ <> ord(']')) or (CharSet = []) then InvalidMask; + WriteScan(msSet); + end; + {$ENDIF} + +begin + {$IFNDEF NEXTGEN} + P := PAnsiChar(AnsiString(Mask)); + I := 0; + Cards := 0; + Reset; + while P^ <> #0 do + begin + if P^ = MatchAnyChar then + SkipTo := True + else + if P^ = MatchSingleChar then + begin + if not SkipTo then WriteScan(msAny); + end + else + case P^ of + '[': ScanSet; + '\': begin + Inc(P); + If P^ <> #0 then + begin + Literal := P^; + WriteScan(msLiteral); + end; + end; + else + if P^ in LeadBytes then + begin + LeadByte := P^; + Inc(P); + TrailByte := P^; + WriteScan(msMBCSLiteral); + end + else + begin + Literal := P^; + WriteScan(msLiteral); + end; + end; + If P^ <> #0 then Inc(P); + end; + Literal := #0; + WriteScan(msLiteral); + Result := I; + {$ELSE} + P := M.AsAnsi(Mask).ToPointer; + I := 0; + Cards := 0; + Reset; + while P^ <> 0 do + begin + if P^ = ord(MatchAnyChar) then + SkipTo := True + else + if P^ = ord(MatchSingleChar) then + begin + if not SkipTo then WriteScan(msAny); + end + else + case P^ of + ord('['): ScanSet; + ord('\'): begin + Inc(P); + If P^ <> 0 then + begin + Literal := P^; + WriteScan(msLiteral); + end; + end; + else + if P^ in LeadBytes then + begin + LeadByte := P^; + Inc(P); + TrailByte := P^; + WriteScan(msMBCSLiteral); + end + else + begin + Literal := P^; + WriteScan(msLiteral); + end; + end; + If P^ <> 0 then Inc(P); + end; + Literal := 0; + WriteScan(msLiteral); + Result := I; + {$ENDIF} +end; + +function MatchesMaskStates(const Filename: DACAString; + const MaskStates: array of TMaskState): Boolean; +type + TStackRec = record + sP: PAnsiDACChar; + sI: Integer; + end; +var + T: Integer; + S: array[0..MaxCards - 1] of TStackRec; + I: Integer; + P: PAnsiDACChar; + {$IFDEF NEXTGEN} + M: TMarshaller; + {$ENDIF} + + procedure Push(P: PAnsiDACChar; I: Integer); + begin + with S[T] do + begin + sP := P; + sI := I; + end; + Inc(T); + end; + + function Pop(var P: PAnsiDACChar; var I: Integer): Boolean; + begin + if T = 0 then + Result := False + else + begin + Dec(T); + with S[T] do + begin + P := sP; + I := sI; + end; + Result := True; + end; + end; + + {$IFNDEF NEXTGEN} + function Matches(P: PAnsiChar; Start: Integer): Boolean; + var + I: Integer; + begin + Result := False; + for I := Start to High(MaskStates) do + with MaskStates[I] do + begin + if SkipTo then + begin + case State of + msLiteral: + while (P^ <> #0) and (P^ <> Literal) do Inc(P); + msSet: + while (P^ <> #0) and not (Negate xor (P^ in CharSet^)) do Inc(P); + msMBCSLiteral: + while (P^ <> #0) do + begin + if (P^ <> LeadByte) then Inc(P, 2) + else + begin + Inc(P); + if (P^ = TrailByte) then Break; + Inc(P); + end; + end; + end; + if P^ <> #0 then Push(@P[1], I); + end; + case State of + msLiteral: if P^ <> Literal then Exit; + msSet: if not (Negate xor (P^ in CharSet^)) then Exit; + msMBCSLiteral: + begin + if P^ <> LeadByte then Exit; + Inc(P); + if P^ <> TrailByte then Exit; + end; + end; + Inc(P); + end; + Result := True; + end; + {$ELSE} + function Matches(P: PAnsiDACBytesChar; Start: Integer): Boolean; + var + I: Integer; + begin + Result := False; + for I := Start to High(MaskStates) do + with MaskStates[I] do + begin + if SkipTo then + begin + case State of + msLiteral: + while (P^ <> 0) and (P^ <> Literal) do Inc(P); + msSet: + while (P^ <> 0) and not (Negate xor (P^ in CharSet^)) do Inc(P); + msMBCSLiteral: + while (P^ <> 0) do + begin + if (P^ <> LeadByte) then Inc(P, 2) + else + begin + Inc(P); + if (P^ = TrailByte) then Break; + Inc(P); + end; + end; + end; + if P^ <> 0 then Push(@P[1], I); + end; + case State of + msLiteral: if P^ <> Literal then Exit; + msSet: if not (Negate xor (P^ in CharSet^)) then Exit; + msMBCSLiteral: + begin + if P^ <> LeadByte then Exit; + Inc(P); + if P^ <> TrailByte then Exit; + end; + end; + Inc(P); + end; + Result := True; + end; + {$ENDIF} + +begin + Result := True; + T := 0; + P := {$IFNDEF NEXTGEN}PAnsiChar(Filename){$ELSE}M.AsAnsi(Filename).ToPointer{$ENDIF}; + I := Low(MaskStates); + repeat + if Matches(PAnsiDACBytesChar(P), I) then Exit; + until not Pop(P, I); + Result := False; +end; + +procedure DoneMaskStates(var MaskStates: array of TMaskState); +var + I: Integer; +begin + for I := Low(MaskStates) to High(MaskStates) do + if MaskStates[I].State = msSet then Dispose(MaskStates[I].CharSet); +end; + +{ TExtMask } + +constructor TExtMask.Create(const MaskValue: string; const CaseSensitive: boolean = False; + const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'); +var + A: array[0..0] of TMaskState; + S: string; +begin + FCaseSensitive := CaseSensitive; + If not FCaseSensitive then S := UpperCase(MaskValue) else S := MaskValue; + FSize := InitMaskStates(S, A, MatchAnyChar, MatchSingleChar); + FMask := AllocMem(FSize * SizeOf(TMaskState)); + InitMaskStates(S, Slice(PMaskStateArray(FMask)^, FSize), MatchAnyChar, MatchSingleChar); +end; + +destructor TExtMask.Destroy; +begin + if FMask <> nil then + begin + DoneMaskStates(Slice(PMaskStateArray(FMask)^, FSize)); + FreeMem(FMask, FSize * SizeOf(TMaskState)); + end; + inherited; +end; + +function TExtMask.Matches(const AString: string): Boolean; +var S: string; +begin + if not FCaseSensitive then S := UpperCase(AString) else S := AString; + Result := MatchesMaskStates(DACAString(S), Slice(PMaskStateArray(FMask)^, FSize)); +end; + +function MatchesMask(const AString, Mask: string; const CaseSensitive: boolean = False; + const MatchAnyChar: AnsiDACChar = '%'; const MatchSingleChar: AnsiDACChar = '?'): Boolean; +var + CMask: TExtMask; +begin + CMask := TExtMask.Create(Mask,CaseSensitive, MatchAnyChar, MatchSingleChar); + try + Result := CMask.Matches(AString); + finally + CMask.Free; + end; +end; + +function EscapeMask(const AMask, EscapeChars: string): string; +var + I, J: Integer; +begin + Result := AMask; + for I := Length(Result) downto 1 do + for J := Length(EscapeChars) downto 1 do + if Result[I] = EscapeChars[J] then + begin + Insert('\', Result, I); + Break; + end; +end; + +end. diff --git a/PSQLFMXConnFrm.fmx b/Source/PSQLFMXConnFrm.fmx similarity index 96% rename from PSQLFMXConnFrm.fmx rename to Source/PSQLFMXConnFrm.fmx index a34e927..fd70e76 100644 --- a/PSQLFMXConnFrm.fmx +++ b/Source/PSQLFMXConnFrm.fmx @@ -1,162 +1,162 @@ -object PSQLFmxConnForm: TPSQLFmxConnForm - Left = 0 - Top = 0 - Caption = 'TPSQLDatabase Editor...' - ClientHeight = 293 - ClientWidth = 334 - Position = MainFormCenter - FormFactor.Width = 320 - FormFactor.Height = 480 - FormFactor.Devices = [Desktop] - DesignerMasterStyle = 0 - object Panel1: TPanel - Align = Top - Size.Width = 334.000000000000000000 - Size.Height = 49.000000000000000000 - Size.PlatformDefault = False - TabOrder = 1 - object Label1: TLabel - Align = HorzCenter - AutoSize = True - StyledSettings = [FontColor] - Position.X = 57.000000000000000000 - Size.Width = 219.000000000000000000 - Size.Height = 49.000000000000000000 - Size.PlatformDefault = False - TextSettings.Font.Family = 'Segoe UI Semibold' - TextSettings.Font.Size = 14.000000000000000000 - TextSettings.Font.StyleExt = {00070000000000000004000000} - TextSettings.WordWrap = False - Text = ' PostgreSQL Connection Options' - end - end - object Panel2: TPanel - Align = Top - Position.Y = 49.000000000000000000 - Size.Width = 334.000000000000000000 - Size.Height = 184.000000000000000000 - Size.PlatformDefault = False - TabOrder = 0 - object DBLogin: TCheckBox - Position.X = 8.000000000000000000 - Position.Y = 152.000000000000000000 - TabOrder = 6 - Text = '&Login Prompt:' - end - object laPort: TLabel - AutoSize = True - Position.X = 9.000000000000000000 - Position.Y = 121.000000000000000000 - Size.Width = 61.000000000000000000 - Size.Height = 16.000000000000000000 - Size.PlatformDefault = False - TextSettings.WordWrap = False - Text = 'Server &Port:' - end - object laHost: TLabel - AutoSize = True - Position.X = 9.000000000000000000 - Position.Y = 95.000000000000000000 - Size.Width = 77.000000000000000000 - Size.Height = 16.000000000000000000 - Size.PlatformDefault = False - TextSettings.WordWrap = False - Text = '&Host Name/IP:' - end - object laPass: TLabel - AutoSize = True - Position.X = 9.000000000000000000 - Position.Y = 69.000000000000000000 - Size.Width = 53.000000000000000000 - Size.Height = 16.000000000000000000 - Size.PlatformDefault = False - TextSettings.WordWrap = False - Text = '&Password:' - end - object lbUser: TLabel - AutoSize = True - Position.X = 9.000000000000000000 - Position.Y = 43.000000000000000000 - Size.Width = 42.000000000000000000 - Size.Height = 16.000000000000000000 - Size.PlatformDefault = False - TextSettings.WordWrap = False - Text = '&User ID:' - end - object laDBName: TLabel - AutoSize = True - Position.X = 9.000000000000000000 - Position.Y = 17.000000000000000000 - Size.Width = 87.000000000000000000 - Size.Height = 16.000000000000000000 - Size.PlatformDefault = False - TextSettings.WordWrap = False - Text = 'Database &Name:' - end - object DBHost: TEdit - Touch.InteractiveGestures = [LongTap, DoubleTap] - TabOrder = 4 - Position.X = 120.000000000000000000 - Position.Y = 92.000000000000000000 - Size.Width = 161.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - end - object DBName: TEdit - Touch.InteractiveGestures = [LongTap, DoubleTap] - TabOrder = 1 - Position.X = 120.000000000000000000 - Position.Y = 14.000000000000000000 - Size.Width = 161.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - end - object DBPasswd: TEdit - Touch.InteractiveGestures = [LongTap, DoubleTap] - TabOrder = 3 - Position.X = 120.000000000000000000 - Position.Y = 66.000000000000000000 - Size.Width = 161.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - end - object DBPort: TEdit - Touch.InteractiveGestures = [LongTap, DoubleTap] - TabOrder = 5 - Position.X = 120.000000000000000000 - Position.Y = 118.000000000000000000 - Size.Width = 161.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - end - object DBUserID: TEdit - Touch.InteractiveGestures = [LongTap, DoubleTap] - TabOrder = 2 - Position.X = 120.000000000000000000 - Position.Y = 40.000000000000000000 - Size.Width = 161.000000000000000000 - Size.Height = 22.000000000000000000 - Size.PlatformDefault = False - end - end - object OKBtn: TButton - ModalResult = 1 - Position.X = 56.000000000000000000 - Position.Y = 248.000000000000000000 - Size.Width = 80.000000000000000000 - Size.Height = 33.000000000000000000 - Size.PlatformDefault = False - TabOrder = 6 - Text = '&OK' - end - object CancelBtn: TButton - ModalResult = 2 - Position.X = 208.000000000000000000 - Position.Y = 248.000000000000000000 - Size.Width = 80.000000000000000000 - Size.Height = 33.000000000000000000 - Size.PlatformDefault = False - TabOrder = 7 - Text = '&Cancel' - end -end +object PSQLFmxConnForm: TPSQLFmxConnForm + Left = 0 + Top = 0 + Caption = 'TPSQLDatabase Editor...' + ClientHeight = 293 + ClientWidth = 334 + Position = MainFormCenter + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + DesignerMasterStyle = 0 + object Panel1: TPanel + Align = Top + Size.Width = 334.000000000000000000 + Size.Height = 49.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object Label1: TLabel + Align = HorzCenter + AutoSize = True + StyledSettings = [FontColor] + Position.X = 57.000000000000000000 + Size.Width = 219.000000000000000000 + Size.Height = 49.000000000000000000 + Size.PlatformDefault = False + TextSettings.Font.Family = 'Segoe UI Semibold' + TextSettings.Font.Size = 14.000000000000000000 + TextSettings.Font.StyleExt = {00070000000000000004000000} + TextSettings.WordWrap = False + Text = ' PostgreSQL Connection Options' + end + end + object Panel2: TPanel + Align = Top + Position.Y = 49.000000000000000000 + Size.Width = 334.000000000000000000 + Size.Height = 184.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + object DBLogin: TCheckBox + Position.X = 8.000000000000000000 + Position.Y = 152.000000000000000000 + TabOrder = 6 + Text = '&Login Prompt:' + end + object laPort: TLabel + AutoSize = True + Position.X = 9.000000000000000000 + Position.Y = 121.000000000000000000 + Size.Width = 61.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + TextSettings.WordWrap = False + Text = 'Server &Port:' + end + object laHost: TLabel + AutoSize = True + Position.X = 9.000000000000000000 + Position.Y = 95.000000000000000000 + Size.Width = 77.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + TextSettings.WordWrap = False + Text = '&Host Name/IP:' + end + object laPass: TLabel + AutoSize = True + Position.X = 9.000000000000000000 + Position.Y = 69.000000000000000000 + Size.Width = 53.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + TextSettings.WordWrap = False + Text = '&Password:' + end + object lbUser: TLabel + AutoSize = True + Position.X = 9.000000000000000000 + Position.Y = 43.000000000000000000 + Size.Width = 42.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + TextSettings.WordWrap = False + Text = '&User ID:' + end + object laDBName: TLabel + AutoSize = True + Position.X = 9.000000000000000000 + Position.Y = 17.000000000000000000 + Size.Width = 87.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + TextSettings.WordWrap = False + Text = 'Database &Name:' + end + object DBHost: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 4 + Position.X = 120.000000000000000000 + Position.Y = 92.000000000000000000 + Size.Width = 161.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + end + object DBName: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 1 + Position.X = 120.000000000000000000 + Position.Y = 14.000000000000000000 + Size.Width = 161.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + end + object DBPasswd: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 3 + Position.X = 120.000000000000000000 + Position.Y = 66.000000000000000000 + Size.Width = 161.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + end + object DBPort: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 5 + Position.X = 120.000000000000000000 + Position.Y = 118.000000000000000000 + Size.Width = 161.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + end + object DBUserID: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 2 + Position.X = 120.000000000000000000 + Position.Y = 40.000000000000000000 + Size.Width = 161.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + end + end + object OKBtn: TButton + ModalResult = 1 + Position.X = 56.000000000000000000 + Position.Y = 248.000000000000000000 + Size.Width = 80.000000000000000000 + Size.Height = 33.000000000000000000 + Size.PlatformDefault = False + TabOrder = 6 + Text = '&OK' + end + object CancelBtn: TButton + ModalResult = 2 + Position.X = 208.000000000000000000 + Position.Y = 248.000000000000000000 + Size.Width = 80.000000000000000000 + Size.Height = 33.000000000000000000 + Size.PlatformDefault = False + TabOrder = 7 + Text = '&Cancel' + end +end diff --git a/PSQLFMXConnFrm.pas b/Source/PSQLFMXConnFrm.pas similarity index 95% rename from PSQLFMXConnFrm.pas rename to Source/PSQLFMXConnFrm.pas index 096da04..2b63a85 100644 --- a/PSQLFMXConnFrm.pas +++ b/Source/PSQLFMXConnFrm.pas @@ -1,60 +1,60 @@ -unit PSQLFMXConnFrm; - -interface - -uses - System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, - FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, - FMX.Controls.Presentation, FMX.Edit, PSQLDbTables, PSQLTypes; - -type - TPSQLFmxConnForm = class(TForm) - Panel1: TPanel; - Panel2: TPanel; - Label1: TLabel; - laDBName: TLabel; - lbUser: TLabel; - laPass: TLabel; - laHost: TLabel; - laPort: TLabel; - DBName: TEdit; - DBPasswd: TEdit; - DBHost: TEdit; - DBUserID: TEdit; - DBPort: TEdit; - DBLogin: TCheckBox; - OKBtn: TButton; - CancelBtn: TButton; - public - procedure GetDatabaseProperty(Db: TPSQLDatabase); - procedure SetDatabaseProperty(Db: TPSQLDatabase); - end; - -var - PSQLFmxConnForm: TPSQLFmxConnForm; - -implementation - -{$R *.fmx} - -procedure TPSQLFmxConnForm.GetDatabaseProperty(Db: TPSQLDatabase); -begin - DBName.Text := DB.DatabaseName; - DBUserId.Text := db.UserName; - DBPasswd.Text := db.UserPassword; - DBHost.Text := Db.Host; - DBPort.Text := IntToStr(Db.Port); - DBLogin.IsChecked := db.LoginPrompt; -end; - -procedure TPSQLFmxConnForm.SetDatabaseProperty(Db: TPSQLDatabase); -begin - DB.DatabaseName := DBName.Text; - db.UserName := DBUserId.Text; - db.UserPassword := DBPasswd.Text; - Db.Host := DBHost.Text; - Db.Port := StrToIntDef(DBPort.Text, PSQLTypes.PSQL_PORT); - db.LoginPrompt := DBLogin.IsChecked; -end; - -end. +unit PSQLFMXConnFrm; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Controls.Presentation, FMX.Edit, PSQLDbTables, PSQLTypes; + +type + TPSQLFmxConnForm = class(TForm) + Panel1: TPanel; + Panel2: TPanel; + Label1: TLabel; + laDBName: TLabel; + lbUser: TLabel; + laPass: TLabel; + laHost: TLabel; + laPort: TLabel; + DBName: TEdit; + DBPasswd: TEdit; + DBHost: TEdit; + DBUserID: TEdit; + DBPort: TEdit; + DBLogin: TCheckBox; + OKBtn: TButton; + CancelBtn: TButton; + public + procedure GetDatabaseProperty(Db: TPSQLDatabase); + procedure SetDatabaseProperty(Db: TPSQLDatabase); + end; + +var + PSQLFmxConnForm: TPSQLFmxConnForm; + +implementation + +{$R *.fmx} + +procedure TPSQLFmxConnForm.GetDatabaseProperty(Db: TPSQLDatabase); +begin + DBName.Text := DB.DatabaseName; + DBUserId.Text := db.UserName; + DBPasswd.Text := db.UserPassword; + DBHost.Text := Db.Host; + DBPort.Text := IntToStr(Db.Port); + DBLogin.IsChecked := db.LoginPrompt; +end; + +procedure TPSQLFmxConnForm.SetDatabaseProperty(Db: TPSQLDatabase); +begin + DB.DatabaseName := DBName.Text; + db.UserName := DBUserId.Text; + db.UserPassword := DBPasswd.Text; + Db.Host := DBHost.Text; + Db.Port := StrToIntDef(DBPort.Text, PSQLTypes.PSQL_PORT); + db.LoginPrompt := DBLogin.IsChecked; +end; + +end. diff --git a/PSQLFields.pas b/Source/PSQLFields.pas similarity index 96% rename from PSQLFields.pas rename to Source/PSQLFields.pas index f53478d..696c161 100644 --- a/PSQLFields.pas +++ b/Source/PSQLFields.pas @@ -1,1058 +1,1058 @@ -{$I pSQLDAC.inc} -unit PSQLFields; - -interface - -uses {$IFDEF UNDER_DELPHI_6}Windows{$ELSE}Types{$ENDIF}, - Classes, DB, PSQLTypes - {$IFDEF DELPHI_12}, PSQLGeomTypes{$ENDIF}; - -{$IFDEF DELPHI_12} - {$NOINCLUDE PSQLGeomTypes} -{$ENDIF} - -type - - { TPSQLPGuidField } - - TPSQLGuidField = class(TGuidField) - protected - class procedure CheckTypeSize(Value: Integer); override; - function GetAsGuid(): TGUID; reintroduce; - procedure SetAsGuid(const Value: TGUID); reintroduce; - public - property AsGuid: TGUID read GetAsGuid write SetAsGuid; - end; - -{$IFDEF DELPHI_12} - - { TPSQLPointField } - - TPSQLPointField = class(TNumericField) - private - function GetAsTPoint: TPoint; - procedure SetAsTPoint(const Value: TPoint); - protected - function GetDataSize: Integer; override; - function GetDefaultWidth: Integer; override; - function GetValue(var Value: TPSQLPoint): Boolean; - function GetAsPoint: TPSQLPoint; - function GetAsString: string; override; - procedure GetText(var Text: string; DisplayText: Boolean); override; - procedure SetAsPoint(const Value: TPSQLPoint); - procedure SetAsString(const Value: string); override; - public - property AsTPoint: TPoint read GetAsTPoint write SetAsTPoint; - property Value: TPSQLPoint read GetAsPoint write SetAsPoint; - end; - - { TPSQLCircleField } - - TPSQLCircleField = class(TNumericField) - protected - function GetDataSize: Integer; override; - function GetDefaultWidth: Integer; override; - function GetValue(var Value: TPSQLCircle): Boolean; - function GetAsCircle: TPSQLCircle; - function GetAsString: string; override; - procedure GetText(var Text: string; DisplayText: Boolean); override; - procedure SetAsCircle(const Value: TPSQLCircle); - procedure SetAsString(const Value: string); override; - public - property Value: TPSQLCircle read GetAsCircle write SetAsCircle; - end; - - { TPSQLBoxField } - - TPSQLBoxField = class(TNumericField) - private - function GetAsTRect: TRect; - procedure SetAsTRect(const Value: TRect); - protected - function GetDataSize: Integer; override; - function GetDefaultWidth: Integer; override; - function GetValue(var Value: TPSQLBox): Boolean; - function GetAsBox: TPSQLBox; - function GetAsString: string; override; - procedure GetText(var Text: string; DisplayText: Boolean); override; - procedure SetAsBox(const Value: TPSQLBox); - procedure SetAsString(const Value: string); override; - public - property AsTRect: TRect read GetAsTRect write SetAsTRect; - property Value: TPSQLBox read GetAsBox write SetAsBox; - end; - - { TPSQLLSegField } - - TPSQLLSegField = class(TNumericField) - protected - function GetDataSize: Integer; override; - function GetDefaultWidth: Integer; override; - function GetValue(var Value: TPSQLLSeg): Boolean; - function GetAsLSeg: TPSQLLSeg; - function GetAsString: string; override; - procedure GetText(var Text: string; DisplayText: Boolean); override; - procedure SetAsLSeg(const Value: TPSQLLSeg); - procedure SetAsString(const Value: string); override; - public - property Value: TPSQLLSeg read GetAsLSeg write SetAsLSeg; - end; - - { TPSQLRangeField } - - TPSQLRangeField = class(TNumericField) - private - function GetValue(var Value: TPSQLRange): Boolean; inline; - function GetAsRange: TPSQLRange; - procedure SetAsRange(const Value: TPSQLRange); - function GetNativeRangeType: cardinal; - protected - function GetAsString: string; override; - procedure SetAsString(const Value: string); override; - function GetDefaultWidth: Integer; override; - public - function IsDiscrete: boolean; virtual; - function IsEmpty: boolean; virtual; - property Value: TPSQLRange read GetAsRange write SetAsRange; - end; - -{$ENDIF DELPHI_12} - -{$IFDEF DELPHI_17} - TPSQLBitConverter = class(TBitConverter) - class function ToPSQLPoint(Value: TArray): TPSQLPoint; - class function ToPSQLCircle(Value: TArray): TPSQLCircle; - class function ToPSQLBox(Value: TArray): TPSQLBox; - class function ToPSQLLSeg(Value: TArray): TPSQLLSeg; - class function ToPSQLRange(Value: TArray): TPSQLRange; - class procedure FromPSQLPoint(Value: TPSQLPoint; Buffer: TArray); - class procedure FromPSQLCircle(Value: TPSQLCircle; Buffer: TArray); - class procedure FromPSQLBox(Value: TPSQLBox; Buffer: TArray); - class procedure FromPSQLLSeg(Value: TPSQLLSeg; Buffer: TArray); - end; -{$ENDIF DELPHI_17} - - -{$IFDEF DELPHI_12} -const - OriginPoint: TPSQLPoint = (X: 0.0; Y: 0.0); - OriginCircle: TPSQLCircle = (R: 0.0; X: 0.0; Y: 0.0); - OriginBox: TPSQLBox = (Right: 0.0; Top: 0.0; Left: 0.0; Bottom: 0.0); - OriginLSeg: TPSQLLSeg = (X1: 0.0; Y1: 0.0; X2: 0.0; Y2: 0.0); -{$ENDIF DELPHI_12} - -procedure Register; - -function BadGUIDToGUID(const AStr: DACAString): DACAString; -{$IFDEF DELPHI_5} -function StringToGUID(const AStr: AnsiString): TGUID; -function IsEqualGUID(const guid1, guid2: TGUID): Boolean; -{$ENDIF} - -implementation - -uses SysUtils, Math, PSQLDBTables; - -procedure Register; -begin - RegisterClasses([TPSQLGuidField - {$IFDEF DELPHI_12} - ,TPSQLPointField, TPSQLCircleField, TPSQLBoxField, TPSQLLSegField, TPSQLRangeField - {$ENDIF DELPHI_12}]); -end; - -{ TPSQLGuidField } -type - TFastGUID = packed record - F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, FA, FB, FC, FD, FE, FF: Byte; - end; - -{$IFDEF DELPHI_17} -class function TPSQLBitConverter.ToPSQLPoint(Value: TArray): TPSQLPoint; -begin - Move(Value[0], Result, SizeOf(TPSQLPoint)); -end; - -class function TPSQLBitConverter.ToPSQLCircle(Value: TArray): TPSQLCircle; -begin - Move(Value[0], Result, SizeOf(TPSQLCircle)); -end; - -class function TPSQLBitConverter.ToPSQLBox(Value: TArray): TPSQLBox; -begin - Move(Value[0], Result, SizeOf(TPSQLBox)); -end; - -class function TPSQLBitConverter.ToPSQLLSeg(Value: TArray): TPSQLLSeg; -begin - Move(Value[0], Result, SizeOf(TPSQLLSeg)); -end; - -class function TPSQLBitConverter.ToPSQLRange(Value: TArray): TPSQLRange; -begin - Move(Value[0], Result, SizeOf(TPSQLRange)); -end; - -class procedure TPSQLBitConverter.FromPSQLPoint(Value: TPSQLPoint; Buffer: TArray); -begin - Move(Value, Buffer[0], SizeOf(TPSQLPoint)); -end; - -class procedure TPSQLBitConverter.FromPSQLCircle(Value: TPSQLCircle; Buffer: TArray); -begin - Move(Value, Buffer[0], SizeOf(TPSQLCircle)); -end; - -class procedure TPSQLBitConverter.FromPSQLBox(Value: TPSQLBox; Buffer: TArray); -begin - Move(Value, Buffer[0], SizeOf(TPSQLBox)); -end; - -class procedure TPSQLBitConverter.FromPSQLLSeg(Value: TPSQLLSeg; Buffer: TArray); -begin - Move(Value, Buffer[0], SizeOf(TPSQLLSeg)); -end; -{$ENDIF} - -{$IFDEF DELPHI_5} - PWord = ^Word; - PSmallInt = ^SmallInt; - PByte = ^Byte; - IntegerArray = array[0..$effffff] of Integer; - PIntegerArray = ^IntegerArray; -{$ENDIF} - -const - GUID_NULL: TGUID = '{00000000-0000-0000-0000-000000000000}'; - - Hex2IntHash: Array [Byte] of Byte = (0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,10, - 11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); - - Int2HexHash : Array [0..255] of Word = ( - $3030, $3130, $3230, $3330, $3430, $3530, $3630, $3730, $3830, $3930, - $4130, $4230, $4330, $4430, $4530, $4630, $3031, $3131, $3231, $3331, - $3431, $3531, $3631, $3731, $3831, $3931, $4131, $4231, $4331, $4431, - $4531, $4631, $3032, $3132, $3232, $3332, $3432, $3532, $3632, $3732, - $3832, $3932, $4132, $4232, $4332, $4432, $4532, $4632, $3033, $3133, - $3233, $3333, $3433, $3533, $3633, $3733, $3833, $3933, $4133, $4233, - $4333, $4433, $4533, $4633, $3034, $3134, $3234, $3334, $3434, $3534, - $3634, $3734, $3834, $3934, $4134, $4234, $4334, $4434, $4534, $4634, - $3035, $3135, $3235, $3335, $3435, $3535, $3635, $3735, $3835, $3935, - $4135, $4235, $4335, $4435, $4535, $4635, $3036, $3136, $3236, $3336, - $3436, $3536, $3636, $3736, $3836, $3936, $4136, $4236, $4336, $4436, - $4536, $4636, $3037, $3137, $3237, $3337, $3437, $3537, $3637, $3737, - $3837, $3937, $4137, $4237, $4337, $4437, $4537, $4637, $3038, $3138, - $3238, $3338, $3438, $3538, $3638, $3738, $3838, $3938, $4138, $4238, - $4338, $4438, $4538, $4638, $3039, $3139, $3239, $3339, $3439, $3539, - $3639, $3739, $3839, $3939, $4139, $4239, $4339, $4439, $4539, $4639, - $3041, $3141, $3241, $3341, $3441, $3541, $3641, $3741, $3841, $3941, - $4141, $4241, $4341, $4441, $4541, $4641, $3042, $3142, $3242, $3342, - $3442, $3542, $3642, $3742, $3842, $3942, $4142, $4242, $4342, $4442, - $4542, $4642, $3043, $3143, $3243, $3343, $3443, $3543, $3643, $3743, - $3843, $3943, $4143, $4243, $4343, $4443, $4543, $4643, $3044, $3144, - $3244, $3344, $3444, $3544, $3644, $3744, $3844, $3944, $4144, $4244, - $4344, $4444, $4544, $4644, $3045, $3145, $3245, $3345, $3445, $3545, - $3645, $3745, $3845, $3945, $4145, $4245, $4345, $4445, $4545, $4645, - $3046, $3146, $3246, $3346, $3446, $3546, $3646, $3746, $3846, $3946, - $4146, $4246, $4346, $4446, $4546, $4646); - -var - HexUpperCase, HexLowerCase: Array[AnsiDACByteChar] of AnsiDACByteChar; - -procedure InitHexUpperCase(); -begin -{$IFNDEF NEXTGEN} - HexUpperCase['a'] := 'A'; - HexUpperCase['b'] := 'B'; - HexUpperCase['c'] := 'C'; - HexUpperCase['d'] := 'D'; - HexUpperCase['e'] := 'E'; - HexUpperCase['f'] := 'F'; - - HexUpperCase['A'] := 'A'; - HexUpperCase['B'] := 'B'; - HexUpperCase['C'] := 'C'; - HexUpperCase['D'] := 'D'; - HexUpperCase['E'] := 'E'; - HexUpperCase['F'] := 'F'; - - HexUpperCase['0'] := '0'; - HexUpperCase['1'] := '1'; - HexUpperCase['2'] := '2'; - HexUpperCase['3'] := '3'; - HexUpperCase['4'] := '4'; - HexUpperCase['5'] := '5'; - HexUpperCase['6'] := '6'; - HexUpperCase['7'] := '7'; - HexUpperCase['8'] := '8'; - HexUpperCase['9'] := '9'; -{$ELSE} - HexUpperCase[Ord('a')] := Ord('A'); - HexUpperCase[Ord('b')] := Ord('B'); - HexUpperCase[Ord('c')] := Ord('C'); - HexUpperCase[Ord('d')] := Ord('D'); - HexUpperCase[Ord('e')] := Ord('E'); - HexUpperCase[Ord('f')] := Ord('F'); - - HexUpperCase[Ord('A')] := Ord('A'); - HexUpperCase[Ord('B')] := Ord('B'); - HexUpperCase[Ord('C')] := Ord('C'); - HexUpperCase[Ord('D')] := Ord('D'); - HexUpperCase[Ord('E')] := Ord('E'); - HexUpperCase[Ord('F')] := Ord('F'); - - HexUpperCase[Ord('0')] := Ord('0'); - HexUpperCase[Ord('1')] := Ord('1'); - HexUpperCase[Ord('2')] := Ord('2'); - HexUpperCase[Ord('3')] := Ord('3'); - HexUpperCase[Ord('4')] := Ord('4'); - HexUpperCase[Ord('5')] := Ord('5'); - HexUpperCase[Ord('6')] := Ord('6'); - HexUpperCase[Ord('7')] := Ord('7'); - HexUpperCase[Ord('8')] := Ord('8'); - HexUpperCase[Ord('9')] := Ord('9'); -{$ENDIF} -end; - -procedure InitHexLowerCase(); -begin -{$IFNDEF NEXTGEN} - HexLowerCase['a'] := 'a'; - HexLowerCase['b'] := 'b'; - HexLowerCase['c'] := 'c'; - HexLowerCase['d'] := 'd'; - HexLowerCase['e'] := 'e'; - HexLowerCase['f'] := 'f'; - - HexLowerCase['A'] := 'a'; - HexLowerCase['B'] := 'b'; - HexLowerCase['C'] := 'c'; - HexLowerCase['D'] := 'd'; - HexLowerCase['E'] := 'e'; - HexLowerCase['F'] := 'f'; - - HexLowerCase['0'] := '0'; - HexLowerCase['1'] := '1'; - HexLowerCase['2'] := '2'; - HexLowerCase['3'] := '3'; - HexLowerCase['4'] := '4'; - HexLowerCase['5'] := '5'; - HexLowerCase['6'] := '6'; - HexLowerCase['7'] := '7'; - HexLowerCase['8'] := '8'; - HexLowerCase['9'] := '9'; -{$ELSE} - HexLowerCase[Ord('a')] := Ord('a'); - HexLowerCase[Ord('b')] := Ord('b'); - HexLowerCase[Ord('c')] := Ord('c'); - HexLowerCase[Ord('d')] := Ord('d'); - HexLowerCase[Ord('e')] := Ord('e'); - HexLowerCase[Ord('f')] := Ord('f'); - - HexLowerCase[Ord('A')] := Ord('a'); - HexLowerCase[Ord('B')] := Ord('b'); - HexLowerCase[Ord('C')] := Ord('c'); - HexLowerCase[Ord('D')] := Ord('d'); - HexLowerCase[Ord('E')] := Ord('e'); - HexLowerCase[Ord('F')] := Ord('f'); - - HexLowerCase[Ord('0')] := Ord('0'); - HexLowerCase[Ord('1')] := Ord('1'); - HexLowerCase[Ord('2')] := Ord('2'); - HexLowerCase[Ord('3')] := Ord('3'); - HexLowerCase[Ord('4')] := Ord('4'); - HexLowerCase[Ord('5')] := Ord('5'); - HexLowerCase[Ord('6')] := Ord('6'); - HexLowerCase[Ord('7')] := Ord('7'); - HexLowerCase[Ord('8')] := Ord('8'); - HexLowerCase[Ord('9')] := Ord('9'); -{$ENDIF} - -end; - -function BadGUIDToGUID(const AStr: DACAString): DACAString; -var - C: PAnsiDACBytesChar; - {$IFDEF MOBILE} - M: TMarshaller; - {$ENDIF} -begin - Result := '{00000000-0000-0000-0000-000000000000}'; - if AStr = '' then Exit; - {$IFDEF MOBILE} - C := M.AsAnsi(AStr).ToPointer; - {$ELSE} - C := PAnsiDACChar(AStr); - {$ENDIF} - - {$IFNDEF NEXTGEN} - Result[2] := HexUpperCase[C^]; Inc(C); - Result[3] := HexUpperCase[C^]; Inc(C); - Result[4] := HexUpperCase[C^]; Inc(C); - Result[5] := HexUpperCase[C^]; Inc(C); - Result[6] := HexUpperCase[C^]; Inc(C); - Result[7] := HexUpperCase[C^]; Inc(C); - Result[8] := HexUpperCase[C^]; Inc(C); - Result[9] := HexUpperCase[C^]; Inc(C); - Inc(C); // skip - - Result[11] := HexUpperCase[C^]; Inc(C); - Result[12] := HexUpperCase[C^]; Inc(C); - Result[13] := HexUpperCase[C^]; Inc(C); - Result[14] := HexUpperCase[C^]; Inc(C); - Inc(C); // skip - - Result[16] := HexUpperCase[C^]; Inc(C); - Result[17] := HexUpperCase[C^]; Inc(C); - Result[18] := HexUpperCase[C^]; Inc(C); - Result[19] := HexUpperCase[C^]; Inc(C); - Inc(C); // skip - - Result[21] := HexUpperCase[C^]; Inc(C); - Result[22] := HexUpperCase[C^]; Inc(C); - Result[23] := HexUpperCase[C^]; Inc(C); - Result[24] := HexUpperCase[C^]; Inc(C); - Inc(C); // skip - - Result[26] := HexUpperCase[C^]; Inc(C); - Result[27] := HexUpperCase[C^]; Inc(C); - Result[28] := HexUpperCase[C^]; Inc(C); - Result[29] := HexUpperCase[C^]; Inc(C); - Result[30] := HexUpperCase[C^]; Inc(C); - Result[31] := HexUpperCase[C^]; Inc(C); - Result[32] := HexUpperCase[C^]; Inc(C); - Result[33] := HexUpperCase[C^]; Inc(C); - Result[34] := HexUpperCase[C^]; Inc(C); - Result[35] := HexUpperCase[C^]; Inc(C); - Result[36] := HexUpperCase[C^]; Inc(C); - Result[37] := HexUpperCase[C^]; - {$ELSE} - Result[1] := Chr(HexUpperCase[C^]); Inc(C); - Result[2] := Chr(HexUpperCase[C^]); Inc(C); - Result[3] := Chr(HexUpperCase[C^]); Inc(C); - Result[4] := Chr(HexUpperCase[C^]); Inc(C); - Result[5] := Chr(HexUpperCase[C^]); Inc(C); - Result[6] := Chr(HexUpperCase[C^]); Inc(C); - Result[7] := Chr(HexUpperCase[C^]); Inc(C); - Result[8] := Chr(HexUpperCase[C^]); Inc(C); - Inc(C); // skip - - Result[10] := Chr(HexUpperCase[C^]); Inc(C); - Result[11] := Chr(HexUpperCase[C^]); Inc(C); - Result[12] := Chr(HexUpperCase[C^]); Inc(C); - Result[13] := Chr(HexUpperCase[C^]); Inc(C); - Inc(C); // skip - - Result[15] := Chr(HexUpperCase[C^]); Inc(C); - Result[16] := Chr(HexUpperCase[C^]); Inc(C); - Result[17] := Chr(HexUpperCase[C^]); Inc(C); - Result[18] := Chr(HexUpperCase[C^]); Inc(C); - Inc(C); // skip - - Result[20] := Chr(HexUpperCase[C^]); Inc(C); - Result[21] := Chr(HexUpperCase[C^]); Inc(C); - Result[22] := Chr(HexUpperCase[C^]); Inc(C); - Result[23] := Chr(HexUpperCase[C^]); Inc(C); - Inc(C); // skip - - Result[25] := Chr(HexUpperCase[C^]); Inc(C); - Result[26] := Chr(HexUpperCase[C^]); Inc(C); - Result[27] := Chr(HexUpperCase[C^]); Inc(C); - Result[28] := Chr(HexUpperCase[C^]); Inc(C); - Result[29] := Chr(HexUpperCase[C^]); Inc(C); - Result[30] := Chr(HexUpperCase[C^]); Inc(C); - Result[31] := Chr(HexUpperCase[C^]); Inc(C); - Result[32] := Chr(HexUpperCase[C^]); Inc(C); - Result[33] := Chr(HexUpperCase[C^]); Inc(C); - Result[34] := Chr(HexUpperCase[C^]); Inc(C); - Result[35] := Chr(HexUpperCase[C^]); Inc(C); - Result[36] := Chr(HexUpperCase[C^]); - - {$ENDIF} -end; - -{$IFNDEF FPC} -function GUIDToString(const AGUID: TGUID): DACAString; -type - TPointerInt = {$IFDEF DELPHI_16}NativeInt{$ELSE}Cardinal{$ENDIF}; -var - P: TPointerInt; - PA: PAnsiDACChar; -begin - // SetLength(Result, 38); - // P := TPointerInt(Result); - PA := DACAnsiStrAlloc(38); - P := TPointerInt(PA); - PByte(P)^ := Ord('{'); Inc(P); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F3]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F2]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F1]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F0]; Inc(P, SizeOf(Word)); - PByte(P)^ := Ord('-'); Inc(P); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F5]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F4]; Inc(P, SizeOf(Word)); - PByte(P)^ := Ord('-'); Inc(P); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F7]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F6]; Inc(P, SizeOf(Word)); - PByte(P)^ := Ord('-'); Inc(P); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F8]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F9]; Inc(P, SizeOf(Word)); - PByte(P)^ := Ord('-'); Inc(P); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FA]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FB]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FC]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FD]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FE]; Inc(P, SizeOf(Word)); - PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FF]; Inc(P, SizeOf(Word)); - PByte(P)^ := Ord('}'); - - Result := {$IFDEF NEXTGEN}String{$ENDIF}(PA); - DACAnsiStrDispose(PA); -end; -{$ENDIF} - -function StringToGUID(const AStr: DACAString): TGUID; -const - strIndexOffset = {$IFNDEF NEXTGEN}0{$ELSE}1{$ENDIF}; -begin - if (AStr = '') or (Length(AStr) <> 38) then begin - Result := GUID_NULL; - Exit; - end; - TFastGUID(Result).F3 := Hex2IntHash[Byte(AStr[2-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[3-strIndexOffset])]; - TFastGUID(Result).F2 := Hex2IntHash[Byte(AStr[4-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[5-strIndexOffset])]; - TFastGUID(Result).F1 := Hex2IntHash[Byte(AStr[6-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[7-strIndexOffset])]; - TFastGUID(Result).F0 := Hex2IntHash[Byte(AStr[8-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[9-strIndexOffset])]; - //- - TFastGUID(Result).F5 := Hex2IntHash[Byte(AStr[11-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[12-strIndexOffset])]; - TFastGUID(Result).F4 := Hex2IntHash[Byte(AStr[13-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[14-strIndexOffset])]; - //- - TFastGUID(Result).F7 := Hex2IntHash[Byte(AStr[16-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[17-strIndexOffset])]; - TFastGUID(Result).F6 := Hex2IntHash[Byte(AStr[18-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[19-strIndexOffset])]; - //- - TFastGUID(Result).F8 := Hex2IntHash[Byte(AStr[21-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[22-strIndexOffset])]; - TFastGUID(Result).F9 := Hex2IntHash[Byte(AStr[23-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[24-strIndexOffset])]; - //- - TFastGUID(Result).FA := Hex2IntHash[Byte(AStr[26-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[27-strIndexOffset])]; - TFastGUID(Result).FB := Hex2IntHash[Byte(AStr[28-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[29-strIndexOffset])]; - TFastGUID(Result).FC := Hex2IntHash[Byte(AStr[30-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[31-strIndexOffset])]; - TFastGUID(Result).FD := Hex2IntHash[Byte(AStr[32-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[33-strIndexOffset])]; - TFastGUID(Result).FE := Hex2IntHash[Byte(AStr[34-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[35-strIndexOffset])]; - TFastGUID(Result).FF := Hex2IntHash[Byte(AStr[36-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[37-strIndexOffset])]; -end; - -function IsEqualGUID(const guid1, guid2: TGUID): Boolean; -var - a, b: PIntegerArray; -begin - a := PIntegerArray(@guid1); - b := PIntegerArray(@guid2); - Result := (a^[0] = b^[0]) and (a^[1] = b^[1]) and (a^[2] = b^[2]) and (a^[3] = b^[3]); -end; - -function TPSQLGuidField.GetAsGuid(): TGUID; -var - S: String; -begin - S := GetAsString(); - Result := StringToGUID(DACAString(S)); -end; - -procedure TPSQLGuidField.SetAsGuid(const Value: TGUID); -begin - if IsEqualGUID(Value, GUID_NULL) - then Clear() - else SetAsString(string(GUIDToString(Value))) -end; - -class procedure TPSQLGuidField.CheckTypeSize(Value: Integer); -begin - if Value < 0 then - inherited; -end; - -{$IFDEF DELPHI_12} - -{ TPSQLPointField } - -function TPSQLPointField.GetAsPoint: TPSQLPoint; -begin - if not GetValue(Result) then - Result := OriginPoint; -end; - -function TPSQLPointField.GetAsString: string; -var - P: TPSQLPoint; -begin - if GetValue(P) then - Result := PointToSQLPoint(P, ';', True) - else - Result := ''; -end; - - -function TPSQLPointField.GetAsTPoint: TPoint; -begin - with GetAsPoint do - begin - Result.X := Longint(Round(X)); - Result.Y := Longint(Round(Y)); - end; -end; - -function TPSQLPointField.GetDataSize: Integer; -begin - Result := SizeOf(TPSQLPoint); -end; - -function TPSQLPointField.GetDefaultWidth: Integer; -begin - Result := 15; -end; - -procedure TPSQLPointField.GetText(var Text: string; DisplayText: Boolean); -var - FmtStr: string; - P: TPSQLPoint; -begin - if GetValue(P) then - begin - if DisplayText or (EditFormat = '') then - FmtStr := DisplayFormat - else - FmtStr := EditFormat; - if FmtStr = '' then - Text := GetAsString() - else - Text := Format(FmtStr, [P.X, P.Y]); - end else - Text := ''; -end; - -{$IFDEF DELPHI_17} -function TPSQLPointField.GetValue(var Value: TPSQLPoint): Boolean; -var - Data: TValueBuffer; -begin - SetLength(Data, SizeOf(TPSQLPoint)); - Result := GetData(Data); - Value := TPSQLBitConverter.ToPSQLPoint(Data); -end; -{$ELSE} -function TPSQLPointField.GetValue(var Value: TPSQLPoint): Boolean; -begin - Result := GetData(@Value); -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLPointField.SetAsPoint(const Value: TPSQLPoint); -begin - SetData(BytesOf(@Value, SizeOf(TPSQLPoint))); -end; -{$ELSE} -procedure TPSQLPointField.SetAsPoint(const Value: TPSQLPoint); -begin - SetData(@Value); -end; -{$ENDIF} - -procedure TPSQLPointField.SetAsString(const Value: string); -var P: TPSQLPoint; -begin - if Value = '' then Clear else - begin - P := SQLPointToPoint(Value, ';', True); - SetAsPoint(P); - end; -end; - -procedure TPSQLPointField.SetAsTPoint(const Value: TPoint); -var P: TPSQLPoint; -begin - P.X := Value.X; - P.Y := Value.Y; - SetAsPoint(P); -end; - -{ TPSQLCircleField } - -function TPSQLCircleField.GetAsCircle: TPSQLCircle; -begin - if not GetValue(Result) then - Result := OriginCircle; -end; - -function TPSQLCircleField.GetAsString: string; -var - P: TPSQLCircle; -begin - if GetValue(P) then - Result := CircleToSQLCircle(P, ';', True) - else - Result := ''; -end; - -function TPSQLCircleField.GetDataSize: Integer; -begin - Result := SizeOf(TPSQLCircle); -end; - -function TPSQLCircleField.GetDefaultWidth: Integer; -begin - Result := 22; -end; - -procedure TPSQLCircleField.GetText(var Text: string; DisplayText: Boolean); -var - FmtStr: string; - P: TPSQLCircle; -begin - if GetValue(P) then - begin - if DisplayText or (EditFormat = '') then - FmtStr := DisplayFormat - else - FmtStr := EditFormat; - if FmtStr = '' then - Text := GetAsString() - else - Text := Format(FmtStr, [P.X, P.Y, P.R]); - end else - Text := ''; -end; - -{$IFDEF DELPHI_17} -function TPSQLCircleField.GetValue(var Value: TPSQLCircle): Boolean; -var - Data: TValueBuffer; -begin - SetLength(Data, SizeOf(TPSQLCircle)); - Result := GetData(Data); - Value := TPSQLBitConverter.ToPSQLCircle(Data); -end; -{$ELSE} -function TPSQLCircleField.GetValue(var Value: TPSQLCircle): Boolean; -begin - Result := GetData(@Value); -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLCircleField.SetAsCircle(const Value: TPSQLCircle); -begin - SetData(BytesOf(@Value, SizeOf(TPSQLCircle))); -end; -{$ELSE} -procedure TPSQLCircleField.SetAsCircle(const Value: TPSQLCircle); -begin - SetData(@Value); -end; -{$ENDIF} - -procedure TPSQLCircleField.SetAsString(const Value: string); -var C: TPSQLCircle; -begin - if Value = '' then Clear else - begin - C := SQLCircleToCircle(Value, ';', True); - SetAsCircle(C); - end; -end; - -{ TPSQLBoxField } - -function TPSQLBoxField.GetAsBox: TPSQLBox; -begin - if not GetValue(Result) then - Result := OriginBox; -end; - -function TPSQLBoxField.GetAsString: string; -var - B: TPSQLBox; -begin - if GetValue(B) then - Result := BoxToSQLBox(B, ';', True) - else - Result := ''; -end; - -function TPSQLBoxField.GetAsTRect: TRect; -begin - with GetAsBox do - begin - Result.Left := Longint(Round(Left)); - Result.Top := Longint(Round(Top)); - Result.Right := Longint(Round(Right)); - Result.Bottom := Longint(Round(Bottom)); - end; -end; - -function TPSQLBoxField.GetDataSize: Integer; -begin - Result := SizeOf(TPSQLBox); -end; - -function TPSQLBoxField.GetDefaultWidth: Integer; -begin - Result := 32; -end; - -procedure TPSQLBoxField.GetText(var Text: string; DisplayText: Boolean); -var - FmtStr: string; - B: TPSQLBox; -begin - if GetValue(B) then - begin - if DisplayText or (EditFormat = '') then - FmtStr := DisplayFormat - else - FmtStr := EditFormat; - if FmtStr = '' then - Text := GetAsString() - else - Text := Format(FmtStr, [B.Left, B.Top, B.Right, B.Bottom]); - end else - Text := ''; -end; - -{$IFDEF DELPHI_17} -function TPSQLBoxField.GetValue(var Value: TPSQLBox): Boolean; -var - Data: TValueBuffer; -begin - SetLength(Data, SizeOf(TPSQLBox)); - Result := GetData(Data); - Value := TPSQLBitConverter.ToPSQLBox(Data); -end; -{$ELSE} -function TPSQLBoxField.GetValue(var Value: TPSQLBox): Boolean; -begin - Result := GetData(@Value); -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLBoxField.SetAsBox(const Value: TPSQLBox); -begin - SetData(BytesOf(@Value, SizeOf(TPSQLBox))); -end; -{$ELSE} -procedure TPSQLBoxField.SetAsBox(const Value: TPSQLBox); -begin - SetData(@Value); -end; -{$ENDIF} - -procedure TPSQLBoxField.SetAsString(const Value: string); -var B: TPSQLBox; -begin - if Value = '' then Clear else - begin - B := SQLBoxToBox(Value, ';', True); - SetAsBox(B); - end; -end; - -procedure TPSQLBoxField.SetAsTRect(const Value: TRect); -var B: TPSQLBox; -begin - B.Right := Value.Right; - B.Top := Value.Top; - B.Left := Value.Left; - B.Bottom := Value.Bottom; - SetAsBox(B); -end; - -{ TPSQLLSegField } - -function TPSQLLSegField.GetAsLSeg: TPSQLLSeg; -begin - if not GetValue(Result) then - Result := OriginLSeg; -end; - -function TPSQLLSegField.GetAsString: string; -var - LS: TPSQLLSeg; -begin - if GetValue(LS) then - Result := LSegToSQLLSeg(LS, ';', True) - else - Result := ''; -end; - -function TPSQLLSegField.GetDataSize: Integer; -begin - Result := SizeOf(TPSQLLSeg); -end; - -function TPSQLLSegField.GetDefaultWidth: Integer; -begin - Result := 32; -end; - -procedure TPSQLLSegField.GetText(var Text: string; DisplayText: Boolean); -var - FmtStr: string; - LS: TPSQLLSeg; -begin - if GetValue(LS) then - begin - if DisplayText or (EditFormat = '') then - FmtStr := DisplayFormat - else - FmtStr := EditFormat; - if FmtStr = '' then - Text := GetAsString() - else - Text := Format(FmtStr, [LS.X1, LS.Y1, LS.X2, LS.Y2]); - end else - Text := ''; -end; - -{$IFDEF DELPHI_17} -function TPSQLLSegField.GetValue(var Value: TPSQLLSeg): Boolean; -var - Data: TValueBuffer; -begin - SetLength(Data, SizeOf(TPSQLLSeg)); - Result := GetData(Data); - Value := TPSQLBitConverter.ToPSQLLSeg(Data); -end; -{$ELSE} -function TPSQLLSegField.GetValue(var Value: TPSQLLSeg): Boolean; -begin - Result := GetData(@Value); -end; -{$ENDIF} - -{$IFDEF DELPHI_17} -procedure TPSQLLSegField.SetAsLSeg(const Value: TPSQLLSeg); -begin - SetData(BytesOf(@Value, SizeOf(TPSQLLSeg))); -end; -{$ELSE} -procedure TPSQLLSegField.SetAsLSeg(const Value: TPSQLLSeg); -begin - SetData(@Value); -end; -{$ENDIF} - -procedure TPSQLLSegField.SetAsString(const Value: string); -var LS: TPSQLLSeg; -begin - if Value = '' then Clear else - begin - LS := SQLLSegToLSeg(Value, ';', True); - SetAsLSeg(LS); - end; -end; - -{ TPSQLRangeField } - -function TPSQLRangeField.GetValue(var Value: TPSQLRange): Boolean; -{$IFDEF DELPHI_17} -var - Data: TValueBuffer; -{$ENDIF} -begin -{$IFDEF DELPHI_17} - SetLength(Data, SizeOf(TPSQLRange)); - Result := GetData(Data); - Value := TPSQLBitConverter.ToPSQLRange(Data); -{$ELSE} - Result := GetData(@Value); -{$ENDIF} -end; - -function TPSQLRangeField.GetNativeRangeType: cardinal; -begin - Result := (DataSet.FieldDefs[FieldNo-1] as TPSQLFieldDef).NativeDataType; -end; - -function TPSQLRangeField.GetAsRange: TPSQLRange; -begin - if not GetValue(Result) then - Result := Default(TPSQLRange); -end; - -function TPSQLRangeField.GetAsString: string; -var R: TPSQLRange; -begin - if GetValue(R) then - Result := RangeToSQLRange(R, GetNativeRangeType(), ';', True) - else - Result := ''; -end; - -procedure TPSQLRangeField.SetAsString(const Value: string); -var R: TPSQLRange; -begin - if Value = '' then Clear else - begin - R := SQLRangeToRange(Value, GetNativeRangeType(), ';', True); - SetAsRange(R); - end; -end; - -function TPSQLRangeField.GetDefaultWidth: Integer; -begin - case GetNativeRangeType() of - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_NUMRANGE : Result := (inherited GetDefaultWidth) * 2 + 3; - FIELD_TYPE_DATERANGE: Result := DATELEN * 2 + 3; - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: Result := TIMESTAMPTZLEN * 2 + 3; - else - Result := 15; - end; -end; - -function TPSQLRangeField.IsDiscrete: boolean; -begin - Result := False; - case GetNativeRangeType() of - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_DATERANGE: Result := True; - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: Result := False; - else - DatabaseErrorFmt(SFieldNotRangeType, [DisplayName]); - end; -end; - -function TPSQLRangeField.IsEmpty: boolean; -var - R: TPSQLRange; -begin - if not GetValue(R) then - Result := True - else - Result := R.Empty; -end; - -procedure TPSQLRangeField.SetAsRange(const Value: TPSQLRange); -begin -{$IFDEF DELPHI_17} - SetData(BytesOf(@Value, SizeOf(TPSQLRange)), True); -{$ELSE} - SetData(@Value); -{$ENDIF} -end; - -{$ENDIF DELPHI_12} - -initialization - -InitHexUpperCase(); -InitHexLowerCase(); - -finalization - -end. +{$I pSQLDAC.inc} +unit PSQLFields; + +interface + +uses {$IFDEF UNDER_DELPHI_6}Windows{$ELSE}Types{$ENDIF}, + Classes, DB, PSQLTypes + {$IFDEF DELPHI_12}, PSQLGeomTypes{$ENDIF}; + +{$IFDEF DELPHI_12} + {$NOINCLUDE PSQLGeomTypes} +{$ENDIF} + +type + + { TPSQLPGuidField } + + TPSQLGuidField = class(TGuidField) + protected + class procedure CheckTypeSize(Value: Integer); override; + function GetAsGuid(): TGUID; reintroduce; + procedure SetAsGuid(const Value: TGUID); reintroduce; + public + property AsGuid: TGUID read GetAsGuid write SetAsGuid; + end; + +{$IFDEF DELPHI_12} + + { TPSQLPointField } + + TPSQLPointField = class(TNumericField) + private + function GetAsTPoint: TPoint; + procedure SetAsTPoint(const Value: TPoint); + protected + function GetDataSize: Integer; override; + function GetDefaultWidth: Integer; override; + function GetValue(var Value: TPSQLPoint): Boolean; + function GetAsPoint: TPSQLPoint; + function GetAsString: string; override; + procedure GetText(var Text: string; DisplayText: Boolean); override; + procedure SetAsPoint(const Value: TPSQLPoint); + procedure SetAsString(const Value: string); override; + public + property AsTPoint: TPoint read GetAsTPoint write SetAsTPoint; + property Value: TPSQLPoint read GetAsPoint write SetAsPoint; + end; + + { TPSQLCircleField } + + TPSQLCircleField = class(TNumericField) + protected + function GetDataSize: Integer; override; + function GetDefaultWidth: Integer; override; + function GetValue(var Value: TPSQLCircle): Boolean; + function GetAsCircle: TPSQLCircle; + function GetAsString: string; override; + procedure GetText(var Text: string; DisplayText: Boolean); override; + procedure SetAsCircle(const Value: TPSQLCircle); + procedure SetAsString(const Value: string); override; + public + property Value: TPSQLCircle read GetAsCircle write SetAsCircle; + end; + + { TPSQLBoxField } + + TPSQLBoxField = class(TNumericField) + private + function GetAsTRect: TRect; + procedure SetAsTRect(const Value: TRect); + protected + function GetDataSize: Integer; override; + function GetDefaultWidth: Integer; override; + function GetValue(var Value: TPSQLBox): Boolean; + function GetAsBox: TPSQLBox; + function GetAsString: string; override; + procedure GetText(var Text: string; DisplayText: Boolean); override; + procedure SetAsBox(const Value: TPSQLBox); + procedure SetAsString(const Value: string); override; + public + property AsTRect: TRect read GetAsTRect write SetAsTRect; + property Value: TPSQLBox read GetAsBox write SetAsBox; + end; + + { TPSQLLSegField } + + TPSQLLSegField = class(TNumericField) + protected + function GetDataSize: Integer; override; + function GetDefaultWidth: Integer; override; + function GetValue(var Value: TPSQLLSeg): Boolean; + function GetAsLSeg: TPSQLLSeg; + function GetAsString: string; override; + procedure GetText(var Text: string; DisplayText: Boolean); override; + procedure SetAsLSeg(const Value: TPSQLLSeg); + procedure SetAsString(const Value: string); override; + public + property Value: TPSQLLSeg read GetAsLSeg write SetAsLSeg; + end; + + { TPSQLRangeField } + + TPSQLRangeField = class(TNumericField) + private + function GetValue(var Value: TPSQLRange): Boolean; inline; + function GetAsRange: TPSQLRange; + procedure SetAsRange(const Value: TPSQLRange); + function GetNativeRangeType: cardinal; + protected + function GetAsString: string; override; + procedure SetAsString(const Value: string); override; + function GetDefaultWidth: Integer; override; + public + function IsDiscrete: boolean; virtual; + function IsEmpty: boolean; virtual; + property Value: TPSQLRange read GetAsRange write SetAsRange; + end; + +{$ENDIF DELPHI_12} + +{$IFDEF DELPHI_17} + TPSQLBitConverter = class(TBitConverter) + class function ToPSQLPoint(Value: TArray): TPSQLPoint; + class function ToPSQLCircle(Value: TArray): TPSQLCircle; + class function ToPSQLBox(Value: TArray): TPSQLBox; + class function ToPSQLLSeg(Value: TArray): TPSQLLSeg; + class function ToPSQLRange(Value: TArray): TPSQLRange; + class procedure FromPSQLPoint(Value: TPSQLPoint; Buffer: TArray); + class procedure FromPSQLCircle(Value: TPSQLCircle; Buffer: TArray); + class procedure FromPSQLBox(Value: TPSQLBox; Buffer: TArray); + class procedure FromPSQLLSeg(Value: TPSQLLSeg; Buffer: TArray); + end; +{$ENDIF DELPHI_17} + + +{$IFDEF DELPHI_12} +const + OriginPoint: TPSQLPoint = (X: 0.0; Y: 0.0); + OriginCircle: TPSQLCircle = (R: 0.0; X: 0.0; Y: 0.0); + OriginBox: TPSQLBox = (Right: 0.0; Top: 0.0; Left: 0.0; Bottom: 0.0); + OriginLSeg: TPSQLLSeg = (X1: 0.0; Y1: 0.0; X2: 0.0; Y2: 0.0); +{$ENDIF DELPHI_12} + +procedure Register; + +function BadGUIDToGUID(const AStr: DACAString): DACAString; +{$IFDEF DELPHI_5} +function StringToGUID(const AStr: AnsiString): TGUID; +function IsEqualGUID(const guid1, guid2: TGUID): Boolean; +{$ENDIF} + +implementation + +uses SysUtils, Math, PSQLDBTables; + +procedure Register; +begin + RegisterClasses([TPSQLGuidField + {$IFDEF DELPHI_12} + ,TPSQLPointField, TPSQLCircleField, TPSQLBoxField, TPSQLLSegField, TPSQLRangeField + {$ENDIF DELPHI_12}]); +end; + +{ TPSQLGuidField } +type + TFastGUID = packed record + F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, FA, FB, FC, FD, FE, FF: Byte; + end; + +{$IFDEF DELPHI_17} +class function TPSQLBitConverter.ToPSQLPoint(Value: TArray): TPSQLPoint; +begin + Move(Value[0], Result, SizeOf(TPSQLPoint)); +end; + +class function TPSQLBitConverter.ToPSQLCircle(Value: TArray): TPSQLCircle; +begin + Move(Value[0], Result, SizeOf(TPSQLCircle)); +end; + +class function TPSQLBitConverter.ToPSQLBox(Value: TArray): TPSQLBox; +begin + Move(Value[0], Result, SizeOf(TPSQLBox)); +end; + +class function TPSQLBitConverter.ToPSQLLSeg(Value: TArray): TPSQLLSeg; +begin + Move(Value[0], Result, SizeOf(TPSQLLSeg)); +end; + +class function TPSQLBitConverter.ToPSQLRange(Value: TArray): TPSQLRange; +begin + Move(Value[0], Result, SizeOf(TPSQLRange)); +end; + +class procedure TPSQLBitConverter.FromPSQLPoint(Value: TPSQLPoint; Buffer: TArray); +begin + Move(Value, Buffer[0], SizeOf(TPSQLPoint)); +end; + +class procedure TPSQLBitConverter.FromPSQLCircle(Value: TPSQLCircle; Buffer: TArray); +begin + Move(Value, Buffer[0], SizeOf(TPSQLCircle)); +end; + +class procedure TPSQLBitConverter.FromPSQLBox(Value: TPSQLBox; Buffer: TArray); +begin + Move(Value, Buffer[0], SizeOf(TPSQLBox)); +end; + +class procedure TPSQLBitConverter.FromPSQLLSeg(Value: TPSQLLSeg; Buffer: TArray); +begin + Move(Value, Buffer[0], SizeOf(TPSQLLSeg)); +end; +{$ENDIF} + +{$IFDEF DELPHI_5} + PWord = ^Word; + PSmallInt = ^SmallInt; + PByte = ^Byte; + IntegerArray = array[0..$effffff] of Integer; + PIntegerArray = ^IntegerArray; +{$ENDIF} + +const + GUID_NULL: TGUID = '{00000000-0000-0000-0000-000000000000}'; + + Hex2IntHash: Array [Byte] of Byte = (0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,10, + 11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + + Int2HexHash : Array [0..255] of Word = ( + $3030, $3130, $3230, $3330, $3430, $3530, $3630, $3730, $3830, $3930, + $4130, $4230, $4330, $4430, $4530, $4630, $3031, $3131, $3231, $3331, + $3431, $3531, $3631, $3731, $3831, $3931, $4131, $4231, $4331, $4431, + $4531, $4631, $3032, $3132, $3232, $3332, $3432, $3532, $3632, $3732, + $3832, $3932, $4132, $4232, $4332, $4432, $4532, $4632, $3033, $3133, + $3233, $3333, $3433, $3533, $3633, $3733, $3833, $3933, $4133, $4233, + $4333, $4433, $4533, $4633, $3034, $3134, $3234, $3334, $3434, $3534, + $3634, $3734, $3834, $3934, $4134, $4234, $4334, $4434, $4534, $4634, + $3035, $3135, $3235, $3335, $3435, $3535, $3635, $3735, $3835, $3935, + $4135, $4235, $4335, $4435, $4535, $4635, $3036, $3136, $3236, $3336, + $3436, $3536, $3636, $3736, $3836, $3936, $4136, $4236, $4336, $4436, + $4536, $4636, $3037, $3137, $3237, $3337, $3437, $3537, $3637, $3737, + $3837, $3937, $4137, $4237, $4337, $4437, $4537, $4637, $3038, $3138, + $3238, $3338, $3438, $3538, $3638, $3738, $3838, $3938, $4138, $4238, + $4338, $4438, $4538, $4638, $3039, $3139, $3239, $3339, $3439, $3539, + $3639, $3739, $3839, $3939, $4139, $4239, $4339, $4439, $4539, $4639, + $3041, $3141, $3241, $3341, $3441, $3541, $3641, $3741, $3841, $3941, + $4141, $4241, $4341, $4441, $4541, $4641, $3042, $3142, $3242, $3342, + $3442, $3542, $3642, $3742, $3842, $3942, $4142, $4242, $4342, $4442, + $4542, $4642, $3043, $3143, $3243, $3343, $3443, $3543, $3643, $3743, + $3843, $3943, $4143, $4243, $4343, $4443, $4543, $4643, $3044, $3144, + $3244, $3344, $3444, $3544, $3644, $3744, $3844, $3944, $4144, $4244, + $4344, $4444, $4544, $4644, $3045, $3145, $3245, $3345, $3445, $3545, + $3645, $3745, $3845, $3945, $4145, $4245, $4345, $4445, $4545, $4645, + $3046, $3146, $3246, $3346, $3446, $3546, $3646, $3746, $3846, $3946, + $4146, $4246, $4346, $4446, $4546, $4646); + +var + HexUpperCase, HexLowerCase: Array[AnsiDACByteChar] of AnsiDACByteChar; + +procedure InitHexUpperCase(); +begin +{$IFNDEF NEXTGEN} + HexUpperCase['a'] := 'A'; + HexUpperCase['b'] := 'B'; + HexUpperCase['c'] := 'C'; + HexUpperCase['d'] := 'D'; + HexUpperCase['e'] := 'E'; + HexUpperCase['f'] := 'F'; + + HexUpperCase['A'] := 'A'; + HexUpperCase['B'] := 'B'; + HexUpperCase['C'] := 'C'; + HexUpperCase['D'] := 'D'; + HexUpperCase['E'] := 'E'; + HexUpperCase['F'] := 'F'; + + HexUpperCase['0'] := '0'; + HexUpperCase['1'] := '1'; + HexUpperCase['2'] := '2'; + HexUpperCase['3'] := '3'; + HexUpperCase['4'] := '4'; + HexUpperCase['5'] := '5'; + HexUpperCase['6'] := '6'; + HexUpperCase['7'] := '7'; + HexUpperCase['8'] := '8'; + HexUpperCase['9'] := '9'; +{$ELSE} + HexUpperCase[Ord('a')] := Ord('A'); + HexUpperCase[Ord('b')] := Ord('B'); + HexUpperCase[Ord('c')] := Ord('C'); + HexUpperCase[Ord('d')] := Ord('D'); + HexUpperCase[Ord('e')] := Ord('E'); + HexUpperCase[Ord('f')] := Ord('F'); + + HexUpperCase[Ord('A')] := Ord('A'); + HexUpperCase[Ord('B')] := Ord('B'); + HexUpperCase[Ord('C')] := Ord('C'); + HexUpperCase[Ord('D')] := Ord('D'); + HexUpperCase[Ord('E')] := Ord('E'); + HexUpperCase[Ord('F')] := Ord('F'); + + HexUpperCase[Ord('0')] := Ord('0'); + HexUpperCase[Ord('1')] := Ord('1'); + HexUpperCase[Ord('2')] := Ord('2'); + HexUpperCase[Ord('3')] := Ord('3'); + HexUpperCase[Ord('4')] := Ord('4'); + HexUpperCase[Ord('5')] := Ord('5'); + HexUpperCase[Ord('6')] := Ord('6'); + HexUpperCase[Ord('7')] := Ord('7'); + HexUpperCase[Ord('8')] := Ord('8'); + HexUpperCase[Ord('9')] := Ord('9'); +{$ENDIF} +end; + +procedure InitHexLowerCase(); +begin +{$IFNDEF NEXTGEN} + HexLowerCase['a'] := 'a'; + HexLowerCase['b'] := 'b'; + HexLowerCase['c'] := 'c'; + HexLowerCase['d'] := 'd'; + HexLowerCase['e'] := 'e'; + HexLowerCase['f'] := 'f'; + + HexLowerCase['A'] := 'a'; + HexLowerCase['B'] := 'b'; + HexLowerCase['C'] := 'c'; + HexLowerCase['D'] := 'd'; + HexLowerCase['E'] := 'e'; + HexLowerCase['F'] := 'f'; + + HexLowerCase['0'] := '0'; + HexLowerCase['1'] := '1'; + HexLowerCase['2'] := '2'; + HexLowerCase['3'] := '3'; + HexLowerCase['4'] := '4'; + HexLowerCase['5'] := '5'; + HexLowerCase['6'] := '6'; + HexLowerCase['7'] := '7'; + HexLowerCase['8'] := '8'; + HexLowerCase['9'] := '9'; +{$ELSE} + HexLowerCase[Ord('a')] := Ord('a'); + HexLowerCase[Ord('b')] := Ord('b'); + HexLowerCase[Ord('c')] := Ord('c'); + HexLowerCase[Ord('d')] := Ord('d'); + HexLowerCase[Ord('e')] := Ord('e'); + HexLowerCase[Ord('f')] := Ord('f'); + + HexLowerCase[Ord('A')] := Ord('a'); + HexLowerCase[Ord('B')] := Ord('b'); + HexLowerCase[Ord('C')] := Ord('c'); + HexLowerCase[Ord('D')] := Ord('d'); + HexLowerCase[Ord('E')] := Ord('e'); + HexLowerCase[Ord('F')] := Ord('f'); + + HexLowerCase[Ord('0')] := Ord('0'); + HexLowerCase[Ord('1')] := Ord('1'); + HexLowerCase[Ord('2')] := Ord('2'); + HexLowerCase[Ord('3')] := Ord('3'); + HexLowerCase[Ord('4')] := Ord('4'); + HexLowerCase[Ord('5')] := Ord('5'); + HexLowerCase[Ord('6')] := Ord('6'); + HexLowerCase[Ord('7')] := Ord('7'); + HexLowerCase[Ord('8')] := Ord('8'); + HexLowerCase[Ord('9')] := Ord('9'); +{$ENDIF} + +end; + +function BadGUIDToGUID(const AStr: DACAString): DACAString; +var + C: PAnsiDACBytesChar; + {$IFDEF MOBILE} + M: TMarshaller; + {$ENDIF} +begin + Result := '{00000000-0000-0000-0000-000000000000}'; + if AStr = '' then Exit; + {$IFDEF MOBILE} + C := M.AsAnsi(AStr).ToPointer; + {$ELSE} + C := PAnsiDACChar(AStr); + {$ENDIF} + + {$IFNDEF NEXTGEN} + Result[2] := HexUpperCase[C^]; Inc(C); + Result[3] := HexUpperCase[C^]; Inc(C); + Result[4] := HexUpperCase[C^]; Inc(C); + Result[5] := HexUpperCase[C^]; Inc(C); + Result[6] := HexUpperCase[C^]; Inc(C); + Result[7] := HexUpperCase[C^]; Inc(C); + Result[8] := HexUpperCase[C^]; Inc(C); + Result[9] := HexUpperCase[C^]; Inc(C); + Inc(C); // skip - + Result[11] := HexUpperCase[C^]; Inc(C); + Result[12] := HexUpperCase[C^]; Inc(C); + Result[13] := HexUpperCase[C^]; Inc(C); + Result[14] := HexUpperCase[C^]; Inc(C); + Inc(C); // skip - + Result[16] := HexUpperCase[C^]; Inc(C); + Result[17] := HexUpperCase[C^]; Inc(C); + Result[18] := HexUpperCase[C^]; Inc(C); + Result[19] := HexUpperCase[C^]; Inc(C); + Inc(C); // skip - + Result[21] := HexUpperCase[C^]; Inc(C); + Result[22] := HexUpperCase[C^]; Inc(C); + Result[23] := HexUpperCase[C^]; Inc(C); + Result[24] := HexUpperCase[C^]; Inc(C); + Inc(C); // skip - + Result[26] := HexUpperCase[C^]; Inc(C); + Result[27] := HexUpperCase[C^]; Inc(C); + Result[28] := HexUpperCase[C^]; Inc(C); + Result[29] := HexUpperCase[C^]; Inc(C); + Result[30] := HexUpperCase[C^]; Inc(C); + Result[31] := HexUpperCase[C^]; Inc(C); + Result[32] := HexUpperCase[C^]; Inc(C); + Result[33] := HexUpperCase[C^]; Inc(C); + Result[34] := HexUpperCase[C^]; Inc(C); + Result[35] := HexUpperCase[C^]; Inc(C); + Result[36] := HexUpperCase[C^]; Inc(C); + Result[37] := HexUpperCase[C^]; + {$ELSE} + Result[1] := Chr(HexUpperCase[C^]); Inc(C); + Result[2] := Chr(HexUpperCase[C^]); Inc(C); + Result[3] := Chr(HexUpperCase[C^]); Inc(C); + Result[4] := Chr(HexUpperCase[C^]); Inc(C); + Result[5] := Chr(HexUpperCase[C^]); Inc(C); + Result[6] := Chr(HexUpperCase[C^]); Inc(C); + Result[7] := Chr(HexUpperCase[C^]); Inc(C); + Result[8] := Chr(HexUpperCase[C^]); Inc(C); + Inc(C); // skip - + Result[10] := Chr(HexUpperCase[C^]); Inc(C); + Result[11] := Chr(HexUpperCase[C^]); Inc(C); + Result[12] := Chr(HexUpperCase[C^]); Inc(C); + Result[13] := Chr(HexUpperCase[C^]); Inc(C); + Inc(C); // skip - + Result[15] := Chr(HexUpperCase[C^]); Inc(C); + Result[16] := Chr(HexUpperCase[C^]); Inc(C); + Result[17] := Chr(HexUpperCase[C^]); Inc(C); + Result[18] := Chr(HexUpperCase[C^]); Inc(C); + Inc(C); // skip - + Result[20] := Chr(HexUpperCase[C^]); Inc(C); + Result[21] := Chr(HexUpperCase[C^]); Inc(C); + Result[22] := Chr(HexUpperCase[C^]); Inc(C); + Result[23] := Chr(HexUpperCase[C^]); Inc(C); + Inc(C); // skip - + Result[25] := Chr(HexUpperCase[C^]); Inc(C); + Result[26] := Chr(HexUpperCase[C^]); Inc(C); + Result[27] := Chr(HexUpperCase[C^]); Inc(C); + Result[28] := Chr(HexUpperCase[C^]); Inc(C); + Result[29] := Chr(HexUpperCase[C^]); Inc(C); + Result[30] := Chr(HexUpperCase[C^]); Inc(C); + Result[31] := Chr(HexUpperCase[C^]); Inc(C); + Result[32] := Chr(HexUpperCase[C^]); Inc(C); + Result[33] := Chr(HexUpperCase[C^]); Inc(C); + Result[34] := Chr(HexUpperCase[C^]); Inc(C); + Result[35] := Chr(HexUpperCase[C^]); Inc(C); + Result[36] := Chr(HexUpperCase[C^]); + + {$ENDIF} +end; + +{$IFNDEF FPC} +function GUIDToString(const AGUID: TGUID): DACAString; +type + TPointerInt = {$IFDEF DELPHI_16}NativeInt{$ELSE}Cardinal{$ENDIF}; +var + P: TPointerInt; + PA: PAnsiDACChar; +begin + // SetLength(Result, 38); + // P := TPointerInt(Result); + PA := DACAnsiStrAlloc(38); + P := TPointerInt(PA); + PByte(P)^ := Ord('{'); Inc(P); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F3]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F2]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F1]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F0]; Inc(P, SizeOf(Word)); + PByte(P)^ := Ord('-'); Inc(P); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F5]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F4]; Inc(P, SizeOf(Word)); + PByte(P)^ := Ord('-'); Inc(P); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F7]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F6]; Inc(P, SizeOf(Word)); + PByte(P)^ := Ord('-'); Inc(P); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F8]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).F9]; Inc(P, SizeOf(Word)); + PByte(P)^ := Ord('-'); Inc(P); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FA]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FB]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FC]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FD]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FE]; Inc(P, SizeOf(Word)); + PWord(P)^ := Int2HexHash[TFastGUID(AGUID).FF]; Inc(P, SizeOf(Word)); + PByte(P)^ := Ord('}'); + + Result := {$IFDEF NEXTGEN}String{$ENDIF}(PA); + DACAnsiStrDispose(PA); +end; +{$ENDIF} + +function StringToGUID(const AStr: DACAString): TGUID; +const + strIndexOffset = {$IFNDEF NEXTGEN}0{$ELSE}1{$ENDIF}; +begin + if (AStr = '') or (Length(AStr) <> 38) then begin + Result := GUID_NULL; + Exit; + end; + TFastGUID(Result).F3 := Hex2IntHash[Byte(AStr[2-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[3-strIndexOffset])]; + TFastGUID(Result).F2 := Hex2IntHash[Byte(AStr[4-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[5-strIndexOffset])]; + TFastGUID(Result).F1 := Hex2IntHash[Byte(AStr[6-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[7-strIndexOffset])]; + TFastGUID(Result).F0 := Hex2IntHash[Byte(AStr[8-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[9-strIndexOffset])]; + //- + TFastGUID(Result).F5 := Hex2IntHash[Byte(AStr[11-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[12-strIndexOffset])]; + TFastGUID(Result).F4 := Hex2IntHash[Byte(AStr[13-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[14-strIndexOffset])]; + //- + TFastGUID(Result).F7 := Hex2IntHash[Byte(AStr[16-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[17-strIndexOffset])]; + TFastGUID(Result).F6 := Hex2IntHash[Byte(AStr[18-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[19-strIndexOffset])]; + //- + TFastGUID(Result).F8 := Hex2IntHash[Byte(AStr[21-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[22-strIndexOffset])]; + TFastGUID(Result).F9 := Hex2IntHash[Byte(AStr[23-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[24-strIndexOffset])]; + //- + TFastGUID(Result).FA := Hex2IntHash[Byte(AStr[26-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[27-strIndexOffset])]; + TFastGUID(Result).FB := Hex2IntHash[Byte(AStr[28-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[29-strIndexOffset])]; + TFastGUID(Result).FC := Hex2IntHash[Byte(AStr[30-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[31-strIndexOffset])]; + TFastGUID(Result).FD := Hex2IntHash[Byte(AStr[32-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[33-strIndexOffset])]; + TFastGUID(Result).FE := Hex2IntHash[Byte(AStr[34-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[35-strIndexOffset])]; + TFastGUID(Result).FF := Hex2IntHash[Byte(AStr[36-strIndexOffset])] shl 4 or Hex2IntHash[Byte(AStr[37-strIndexOffset])]; +end; + +function IsEqualGUID(const guid1, guid2: TGUID): Boolean; +var + a, b: PIntegerArray; +begin + a := PIntegerArray(@guid1); + b := PIntegerArray(@guid2); + Result := (a^[0] = b^[0]) and (a^[1] = b^[1]) and (a^[2] = b^[2]) and (a^[3] = b^[3]); +end; + +function TPSQLGuidField.GetAsGuid(): TGUID; +var + S: String; +begin + S := GetAsString(); + Result := StringToGUID(DACAString(S)); +end; + +procedure TPSQLGuidField.SetAsGuid(const Value: TGUID); +begin + if IsEqualGUID(Value, GUID_NULL) + then Clear() + else SetAsString(string(GUIDToString(Value))) +end; + +class procedure TPSQLGuidField.CheckTypeSize(Value: Integer); +begin + if Value < 0 then + inherited; +end; + +{$IFDEF DELPHI_12} + +{ TPSQLPointField } + +function TPSQLPointField.GetAsPoint: TPSQLPoint; +begin + if not GetValue(Result) then + Result := OriginPoint; +end; + +function TPSQLPointField.GetAsString: string; +var + P: TPSQLPoint; +begin + if GetValue(P) then + Result := PointToSQLPoint(P, ';', True) + else + Result := ''; +end; + + +function TPSQLPointField.GetAsTPoint: TPoint; +begin + with GetAsPoint do + begin + Result.X := Longint(Round(X)); + Result.Y := Longint(Round(Y)); + end; +end; + +function TPSQLPointField.GetDataSize: Integer; +begin + Result := SizeOf(TPSQLPoint); +end; + +function TPSQLPointField.GetDefaultWidth: Integer; +begin + Result := 15; +end; + +procedure TPSQLPointField.GetText(var Text: string; DisplayText: Boolean); +var + FmtStr: string; + P: TPSQLPoint; +begin + if GetValue(P) then + begin + if DisplayText or (EditFormat = '') then + FmtStr := DisplayFormat + else + FmtStr := EditFormat; + if FmtStr = '' then + Text := GetAsString() + else + Text := Format(FmtStr, [P.X, P.Y]); + end else + Text := ''; +end; + +{$IFDEF DELPHI_17} +function TPSQLPointField.GetValue(var Value: TPSQLPoint): Boolean; +var + Data: TValueBuffer; +begin + SetLength(Data, SizeOf(TPSQLPoint)); + Result := GetData(Data); + Value := TPSQLBitConverter.ToPSQLPoint(Data); +end; +{$ELSE} +function TPSQLPointField.GetValue(var Value: TPSQLPoint): Boolean; +begin + Result := GetData(@Value); +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLPointField.SetAsPoint(const Value: TPSQLPoint); +begin + SetData(BytesOf(@Value, SizeOf(TPSQLPoint))); +end; +{$ELSE} +procedure TPSQLPointField.SetAsPoint(const Value: TPSQLPoint); +begin + SetData(@Value); +end; +{$ENDIF} + +procedure TPSQLPointField.SetAsString(const Value: string); +var P: TPSQLPoint; +begin + if Value = '' then Clear else + begin + P := SQLPointToPoint(Value, ';', True); + SetAsPoint(P); + end; +end; + +procedure TPSQLPointField.SetAsTPoint(const Value: TPoint); +var P: TPSQLPoint; +begin + P.X := Value.X; + P.Y := Value.Y; + SetAsPoint(P); +end; + +{ TPSQLCircleField } + +function TPSQLCircleField.GetAsCircle: TPSQLCircle; +begin + if not GetValue(Result) then + Result := OriginCircle; +end; + +function TPSQLCircleField.GetAsString: string; +var + P: TPSQLCircle; +begin + if GetValue(P) then + Result := CircleToSQLCircle(P, ';', True) + else + Result := ''; +end; + +function TPSQLCircleField.GetDataSize: Integer; +begin + Result := SizeOf(TPSQLCircle); +end; + +function TPSQLCircleField.GetDefaultWidth: Integer; +begin + Result := 22; +end; + +procedure TPSQLCircleField.GetText(var Text: string; DisplayText: Boolean); +var + FmtStr: string; + P: TPSQLCircle; +begin + if GetValue(P) then + begin + if DisplayText or (EditFormat = '') then + FmtStr := DisplayFormat + else + FmtStr := EditFormat; + if FmtStr = '' then + Text := GetAsString() + else + Text := Format(FmtStr, [P.X, P.Y, P.R]); + end else + Text := ''; +end; + +{$IFDEF DELPHI_17} +function TPSQLCircleField.GetValue(var Value: TPSQLCircle): Boolean; +var + Data: TValueBuffer; +begin + SetLength(Data, SizeOf(TPSQLCircle)); + Result := GetData(Data); + Value := TPSQLBitConverter.ToPSQLCircle(Data); +end; +{$ELSE} +function TPSQLCircleField.GetValue(var Value: TPSQLCircle): Boolean; +begin + Result := GetData(@Value); +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLCircleField.SetAsCircle(const Value: TPSQLCircle); +begin + SetData(BytesOf(@Value, SizeOf(TPSQLCircle))); +end; +{$ELSE} +procedure TPSQLCircleField.SetAsCircle(const Value: TPSQLCircle); +begin + SetData(@Value); +end; +{$ENDIF} + +procedure TPSQLCircleField.SetAsString(const Value: string); +var C: TPSQLCircle; +begin + if Value = '' then Clear else + begin + C := SQLCircleToCircle(Value, ';', True); + SetAsCircle(C); + end; +end; + +{ TPSQLBoxField } + +function TPSQLBoxField.GetAsBox: TPSQLBox; +begin + if not GetValue(Result) then + Result := OriginBox; +end; + +function TPSQLBoxField.GetAsString: string; +var + B: TPSQLBox; +begin + if GetValue(B) then + Result := BoxToSQLBox(B, ';', True) + else + Result := ''; +end; + +function TPSQLBoxField.GetAsTRect: TRect; +begin + with GetAsBox do + begin + Result.Left := Longint(Round(Left)); + Result.Top := Longint(Round(Top)); + Result.Right := Longint(Round(Right)); + Result.Bottom := Longint(Round(Bottom)); + end; +end; + +function TPSQLBoxField.GetDataSize: Integer; +begin + Result := SizeOf(TPSQLBox); +end; + +function TPSQLBoxField.GetDefaultWidth: Integer; +begin + Result := 32; +end; + +procedure TPSQLBoxField.GetText(var Text: string; DisplayText: Boolean); +var + FmtStr: string; + B: TPSQLBox; +begin + if GetValue(B) then + begin + if DisplayText or (EditFormat = '') then + FmtStr := DisplayFormat + else + FmtStr := EditFormat; + if FmtStr = '' then + Text := GetAsString() + else + Text := Format(FmtStr, [B.Left, B.Top, B.Right, B.Bottom]); + end else + Text := ''; +end; + +{$IFDEF DELPHI_17} +function TPSQLBoxField.GetValue(var Value: TPSQLBox): Boolean; +var + Data: TValueBuffer; +begin + SetLength(Data, SizeOf(TPSQLBox)); + Result := GetData(Data); + Value := TPSQLBitConverter.ToPSQLBox(Data); +end; +{$ELSE} +function TPSQLBoxField.GetValue(var Value: TPSQLBox): Boolean; +begin + Result := GetData(@Value); +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLBoxField.SetAsBox(const Value: TPSQLBox); +begin + SetData(BytesOf(@Value, SizeOf(TPSQLBox))); +end; +{$ELSE} +procedure TPSQLBoxField.SetAsBox(const Value: TPSQLBox); +begin + SetData(@Value); +end; +{$ENDIF} + +procedure TPSQLBoxField.SetAsString(const Value: string); +var B: TPSQLBox; +begin + if Value = '' then Clear else + begin + B := SQLBoxToBox(Value, ';', True); + SetAsBox(B); + end; +end; + +procedure TPSQLBoxField.SetAsTRect(const Value: TRect); +var B: TPSQLBox; +begin + B.Right := Value.Right; + B.Top := Value.Top; + B.Left := Value.Left; + B.Bottom := Value.Bottom; + SetAsBox(B); +end; + +{ TPSQLLSegField } + +function TPSQLLSegField.GetAsLSeg: TPSQLLSeg; +begin + if not GetValue(Result) then + Result := OriginLSeg; +end; + +function TPSQLLSegField.GetAsString: string; +var + LS: TPSQLLSeg; +begin + if GetValue(LS) then + Result := LSegToSQLLSeg(LS, ';', True) + else + Result := ''; +end; + +function TPSQLLSegField.GetDataSize: Integer; +begin + Result := SizeOf(TPSQLLSeg); +end; + +function TPSQLLSegField.GetDefaultWidth: Integer; +begin + Result := 32; +end; + +procedure TPSQLLSegField.GetText(var Text: string; DisplayText: Boolean); +var + FmtStr: string; + LS: TPSQLLSeg; +begin + if GetValue(LS) then + begin + if DisplayText or (EditFormat = '') then + FmtStr := DisplayFormat + else + FmtStr := EditFormat; + if FmtStr = '' then + Text := GetAsString() + else + Text := Format(FmtStr, [LS.X1, LS.Y1, LS.X2, LS.Y2]); + end else + Text := ''; +end; + +{$IFDEF DELPHI_17} +function TPSQLLSegField.GetValue(var Value: TPSQLLSeg): Boolean; +var + Data: TValueBuffer; +begin + SetLength(Data, SizeOf(TPSQLLSeg)); + Result := GetData(Data); + Value := TPSQLBitConverter.ToPSQLLSeg(Data); +end; +{$ELSE} +function TPSQLLSegField.GetValue(var Value: TPSQLLSeg): Boolean; +begin + Result := GetData(@Value); +end; +{$ENDIF} + +{$IFDEF DELPHI_17} +procedure TPSQLLSegField.SetAsLSeg(const Value: TPSQLLSeg); +begin + SetData(BytesOf(@Value, SizeOf(TPSQLLSeg))); +end; +{$ELSE} +procedure TPSQLLSegField.SetAsLSeg(const Value: TPSQLLSeg); +begin + SetData(@Value); +end; +{$ENDIF} + +procedure TPSQLLSegField.SetAsString(const Value: string); +var LS: TPSQLLSeg; +begin + if Value = '' then Clear else + begin + LS := SQLLSegToLSeg(Value, ';', True); + SetAsLSeg(LS); + end; +end; + +{ TPSQLRangeField } + +function TPSQLRangeField.GetValue(var Value: TPSQLRange): Boolean; +{$IFDEF DELPHI_17} +var + Data: TValueBuffer; +{$ENDIF} +begin +{$IFDEF DELPHI_17} + SetLength(Data, SizeOf(TPSQLRange)); + Result := GetData(Data); + Value := TPSQLBitConverter.ToPSQLRange(Data); +{$ELSE} + Result := GetData(@Value); +{$ENDIF} +end; + +function TPSQLRangeField.GetNativeRangeType: cardinal; +begin + Result := (DataSet.FieldDefs[FieldNo-1] as TPSQLFieldDef).NativeDataType; +end; + +function TPSQLRangeField.GetAsRange: TPSQLRange; +begin + if not GetValue(Result) then + Result := Default(TPSQLRange); +end; + +function TPSQLRangeField.GetAsString: string; +var R: TPSQLRange; +begin + if GetValue(R) then + Result := RangeToSQLRange(R, GetNativeRangeType(), ';', True) + else + Result := ''; +end; + +procedure TPSQLRangeField.SetAsString(const Value: string); +var R: TPSQLRange; +begin + if Value = '' then Clear else + begin + R := SQLRangeToRange(Value, GetNativeRangeType(), ';', True); + SetAsRange(R); + end; +end; + +function TPSQLRangeField.GetDefaultWidth: Integer; +begin + case GetNativeRangeType() of + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_NUMRANGE : Result := (inherited GetDefaultWidth) * 2 + 3; + FIELD_TYPE_DATERANGE: Result := DATELEN * 2 + 3; + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: Result := TIMESTAMPTZLEN * 2 + 3; + else + Result := 15; + end; +end; + +function TPSQLRangeField.IsDiscrete: boolean; +begin + Result := False; + case GetNativeRangeType() of + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_DATERANGE: Result := True; + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: Result := False; + else + DatabaseErrorFmt(SFieldNotRangeType, [DisplayName]); + end; +end; + +function TPSQLRangeField.IsEmpty: boolean; +var + R: TPSQLRange; +begin + if not GetValue(R) then + Result := True + else + Result := R.Empty; +end; + +procedure TPSQLRangeField.SetAsRange(const Value: TPSQLRange); +begin +{$IFDEF DELPHI_17} + SetData(BytesOf(@Value, SizeOf(TPSQLRange)), True); +{$ELSE} + SetData(@Value); +{$ENDIF} +end; + +{$ENDIF DELPHI_12} + +initialization + +InitHexUpperCase(); +InitHexLowerCase(); + +finalization + +end. diff --git a/PSQLGeomTypes.pas b/Source/PSQLGeomTypes.pas similarity index 97% rename from PSQLGeomTypes.pas rename to Source/PSQLGeomTypes.pas index 7f59e91..8015abb 100644 --- a/PSQLGeomTypes.pas +++ b/Source/PSQLGeomTypes.pas @@ -1,338 +1,338 @@ -{$I pSQLDAC.inc} -unit PSQLGeomTypes; - -interface - -{$IFDEF DELPHI_12} -uses Types; - -type - TPSQLRangeBoundState = (rbsExclusive, rbsInclusive, rbsInfinite); - - TPSQLRangeBoundValue = record - case Integer of - 0: (IVal: Integer); - 1: (FVal: Double); - 2: (DVal: TDateTime); - 3: (LVal: Int64); - //4: (TVal: TSQLTimeStamp); for future use - end; - PPSQLRangeBoundValue = ^TPSQLRangeBoundValue; - - TPSQLRangeBound = packed record - strict private - var Value: TPSQLRangeBoundValue; - public - State: TPSQLRangeBoundState; - property AsInteger: integer read Value.IVal write Value.IVal; - property AsFloat: double read Value.FVal write Value.FVal; - property AsLargeInt: Int64 read Value.LVal write Value.LVal; - property AsDateTime: TDateTime read Value.DVal write Value.DVal; - //property AsSQLTimeStamp: TSQLTimeStamp read Value.TVal write Value.TVal; for future use - class operator Equal(RB1: TPSQLRangeBound; RB2: TPSQLRangeBound): Boolean; - end; - - TPSQLRange = packed record - private - function GetEmpty: boolean; - public - LowerBound: TPSQLRangeBound; - UpperBound: TPSQLRangeBound; - property Empty: boolean read GetEmpty; - procedure SetEmpty; - class operator Equal(R1: TPSQLRange; R2: TPSQLRange): Boolean; - constructor Create(const Value: string; const RangeType: cardinal); - end; - - TPSQLPoint = packed record - X: Double; - Y: Double; - class operator Equal(P1: TPSQLPoint; P2: TPSQLPoint): Boolean; - class operator Implicit(P: TPoint): TPSQLPoint; - end; - - TPSQLCircle = packed record - R: Double; - class operator Equal(C1: TPSQLCircle; C2: TPSQLCircle): Boolean; - case Integer of - 0: (X, Y: Double); - 1: (Center: TPSQLPoint); - end; - - TPSQLBox = packed record - class operator Equal(B1: TPSQLBox; B2: TPSQLBox): Boolean; - case Integer of - 0: (Right, Top, Left, Bottom: Double); - 1: (TopRight, BottomLeft: TPSQLPoint); - end; - - TPSQLLSeg = packed record - class operator Equal(L1: TPSQLLSeg; L2: TPSQLLSeg): Boolean; - case Integer of - 0: (X1, Y1, X2, Y2: Double); - 1: (P1, P2: TPSQLPoint); - end; - -function SQLPointToPoint(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLPoint; -function PointToSQLPoint(Value: TPSQLPoint; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -function SQLCircleToCircle(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLCircle; -function CircleToSQLCircle(Value: TPSQLCircle; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -function SQLBoxToBox(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLBox; -function BoxToSQLBox(Value: TPSQLBox; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -function SQLLSegToLSeg(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLLSeg; -function LSegToSQLLSeg(Value: TPSQLLSeg; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -function SQLRangeToRange(Value: string; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLRange; -function RangeToSQLRange(Value: TPSQLRange; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): string; -{$ENDIF DELPHI_12} - -implementation - -{$IFDEF DELPHI_12} -uses PSQLAccess, PSQLTypes, SysUtils, StrUtils, Math; - -function SQLPointToPoint(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLPoint; -var S, Xs, Ys: string; - DelimPos: integer; -begin - S := Copy(Value, 2, Length(Value) - 2); //eliminate brackets - DelimPos := Pos(Delimiter, S); - Xs := Copy(S, 1, DelimPos - 1); - Ys := Copy(S, DelimPos + 1, MaxInt); - if not UseSystemSeparator then - begin - Result.X := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Xs, PSQL_FS); - Result.Y := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Ys, PSQL_FS); - end - else - begin - Result.X := {$IFDEF UNDER_DELPHI_6}SysUtils.{$ENDIF}StrToFloat(Xs); - Result.Y := {$IFDEF UNDER_DELPHI_6}SysUtils.{$ENDIF}StrToFloat(Ys); - end; -end; - -function PointToSQLPoint(Value: TPSQLPoint; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -begin - if UseSystemSeparator then - Result := Format('(%g' + Delimiter +'%g)', [Value.X, Value.Y]) - else - Result := '(' + SQLFloatToStr(Value.X) + Delimiter + SQLFloatToStr(Value.Y) + ')'; -end; - -function SQLCircleToCircle(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLCircle; -var S, Rs: string; - DelimPos: integer; -begin - S := Copy(Value, 2, Length(Value) - 2); //eliminate <> brackets - DelimPos := Pos(')', S); - Result.Center := SQLPointToPoint(Copy(S, 1, DelimPos), Delimiter, UseSystemSeparator); - Rs := Copy(S, DelimPos + 2, MaxInt); //closing bracket plus delimiter - if not UseSystemSeparator then - Result.R := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Rs, PSQL_FS) - else - Result.R := {$IFDEF UNDER_DELPHI_6}SysUtils.{$ENDIF}StrToFloat(Rs); -end; - -function CircleToSQLCircle(Value: TPSQLCircle; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -begin - Result := PointToSQLPoint(Value.Center, Delimiter, UseSystemSeparator); - with Value do - if UseSystemSeparator then - Result := '<' + Result + Delimiter + Format('%g>', [R]) - else - Result := '<' + Result + Delimiter + SQLFloatToStr(R) + '>'; -end; - -function SQLBoxToBox(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLBox; -var DelimPos: integer; -begin - DelimPos := Pos(')', Value) + 1; - Result.TopRight := SQLPointToPoint(Copy(Value, 1, DelimPos - 1), Delimiter, UseSystemSeparator); - Result.BottomLeft := SQLPointToPoint(Copy(Value, DelimPos + 1, MaxInt), Delimiter, UseSystemSeparator); -end; - -function BoxToSQLBox(Value: TPSQLBox; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -begin - Result := PointToSQLPoint(Value.TopRight, Delimiter, UseSystemSeparator) + Delimiter + PointToSQLPoint(Value.BottomLeft, Delimiter, UseSystemSeparator); -end; - -function SQLLSegToLSeg(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLLSeg; -var - DelimPos: integer; - S: string; -begin - S := Copy(Value, 2, Length(Value) - 2); //eliminate [] brackets - DelimPos := Pos(')', S) + 1; - Result.P1 := SQLPointToPoint(Copy(S, 1, DelimPos - 1), Delimiter, UseSystemSeparator); - Result.P2 := SQLPointToPoint(Copy(S, DelimPos + 1, MaxInt), Delimiter, UseSystemSeparator); -end; - -function LSegToSQLLSeg(Value: TPSQLLSeg; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; -begin - Result := '[' + PointToSQLPoint(Value.P1, Delimiter, UseSystemSeparator) + Delimiter + PointToSQLPoint(Value.P2, Delimiter, UseSystemSeparator) + ']'; -end; - -function SQLRangeToRange(Value: string; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLRange; -var - DelimPos: integer; - - procedure SetBound(var B: TPSQLRangeBound; SVal: string); - begin - if SVal = EmptyStr then - B.State := rbsInfinite - else - case RangeType of - FIELD_TYPE_NUMRANGE: if not UseSystemSeparator then - B.AsFloat := StrToFloat(SVal, PSQL_FS) - else - B.AsFloat := StrToFloat(SVal); - FIELD_TYPE_INT4RANGE: B.AsInteger := StrToInt(SVal); - FIELD_TYPE_INT8RANGE: B.AsLargeInt := StrToInt64(SVal); - FIELD_TYPE_DATERANGE: if UseSystemSeparator then - B.AsDateTime := StrToDate(SVal) - else - B.AsDateTime := SqlDateToDateTime(SVal, False); - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: if UseSystemSeparator then - B.AsDateTime := StrToDateTime(SVal) - else - B.AsDateTime := SQLTimeStampToDateTime(SVal); - end; - end; - -begin - Result := Default(TPSQLRange); - if SameText(Value, 'empty') or (Length(Value) <= 2) then - Result.SetEmpty - else - begin - Result.LowerBound.State := TPSQLRangeBoundState(ifthen(Value[START_STR_INDEX] = '[', ord(rbsInclusive), ord(rbsExclusive))); - Result.UpperBound.State := TPSQLRangeBoundState(ifthen(Value[Length(Value){$IFDEF ZEROBASEDSTRINGS}-1{$ENDIF}] = ']', ord(rbsInclusive), ord(rbsExclusive))); - Value := Copy(Value, 2, Length(Value) - 2); //eliminate brackets - DelimPos := Pos(Delimiter, Value); - SetBound(Result.LowerBound, AnsiDequotedStr(Copy(Value, 1, DelimPos - 1), '"')); // eliminate quotes if needed - SetBound(Result.UpperBound, AnsiDequotedStr(Copy(Value, DelimPos + 1, MaxInt), '"')); - end; -end; - -function RangeToSQLRange(Value: TPSQLRange; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): string; - - function GetBound(const B: TPSQLRangeBound): string; - begin - case RangeType of - FIELD_TYPE_INT4RANGE: Result := IntToStr(B.AsInteger); - FIELD_TYPE_INT8RANGE: Result := IntToStr(B.AsLargeInt); - FIELD_TYPE_NUMRANGE: if UseSystemSeparator then - Result := FloatToStr(B.AsFloat) - else - Result := SQLFloatToStr(B.AsFloat); - FIELD_TYPE_DATERANGE: if UseSystemSeparator then - Result := DateToStr(B.AsDateTime) - else - Result := DateTimeToSqlDate(B.AsDateTime, DATE_MODE); - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: if UseSystemSeparator then - Result := DateTimeToStr(B.AsDateTime) - else - Result := DateTimeToSqlDate(B.AsDateTime, TIMESTAMP_MODE); - end; - end; - -begin - if Value.Empty then Exit('empty'); - if Value.LowerBound.State = rbsInfinite then - Result := '[' - else - begin - Result := ifthen(Value.LowerBound.State = rbsInclusive, '[', '('); - Result := Result + GetBound(Value.LowerBound); - end; - Result := Result + Delimiter; - if Value.UpperBound.State = rbsInfinite then - Result := Result + ']' - else - begin - Result := Result + GetBound(Value.UpperBound); - Result := Result + ifthen(Value.UpperBound.State = rbsInclusive, ']', ')'); - end; -end; - -{ TPSQLRange } - -class operator TPSQLRangeBound.Equal(RB1: TPSQLRangeBound; RB2: TPSQLRangeBound): Boolean; -begin - Result := (RB1.State = RB2.State) - and CompareMem(@RB1.Value, @RB2.Value, SizeOf(TPSQLRangeBoundValue)); //timestamp is the biggest field in record -end; - -constructor TPSQLRange.Create(const Value: string; const RangeType: cardinal); -begin - case RangeType of - FIELD_TYPE_DATERANGE, - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: Self := SQLRangeToRange(Value, RangeType); - else - raise EConvertError.Create(SInvalidRangeType); - end; -end; - -class operator TPSQLRange.Equal(R1: TPSQLRange; R2: TPSQLRange): Boolean; -begin - Result := (R1.Empty = R2.Empty) and (R1.LowerBound = R2.LowerBound) and (R1.UpperBound = R2.UpperBound); -end; - -function TPSQLRange.GetEmpty: boolean; -begin - Result := (LowerBound.State = rbsExclusive) and (LowerBound = UpperBound); -end; - -{ TPSQLPoint } - -class operator TPSQLPoint.Equal(P1, P2: TPSQLPoint): Boolean; -begin - Result := SameValue(P1.X, P2.X, 0) and SameValue(P1.Y, P2.Y, 0); -end; - -class operator TPSQLPoint.Implicit(P: TPoint): TPSQLPoint; -begin - Result.X := P.X; - Result.Y := P.Y; -end; - -{ TPSQLCircle } - -class operator TPSQLCircle.Equal(C1: TPSQLCircle; C2: TPSQLCircle): Boolean; -begin - Result := (C1.Center = C2.Center) and SameValue(C1.R, C2.R); -end; - -{ TPSQLBox } - -class operator TPSQLBox.Equal(B1, B2: TPSQLBox): Boolean; -begin - Result := (B1.TopRight = B2.TopRight) and (B1.BottomLeft = B2.BottomLeft); -end; - -{ TPSQLLSeg } - -class operator TPSQLLSeg.Equal(L1: TPSQLLSeg; L2: TPSQLLSeg): Boolean; -begin - Result := (L1.P1 = L2.P1) and (L1.P2 = L2.P2); -end; - -{TPSQLRange} - -procedure TPSQLRange.SetEmpty; -begin - UpperBound.State := rbsExclusive; - LowerBound.State := rbsExclusive; -// UpperBound.AsSQLTimeStamp := NullSQLTimeStamp; -// LowerBound.AsSQLTimeStamp := NullSQLTimeStamp; -end; - -{$ENDIF DELPHI_12} - - -end. +{$I pSQLDAC.inc} +unit PSQLGeomTypes; + +interface + +{$IFDEF DELPHI_12} +uses Types; + +type + TPSQLRangeBoundState = (rbsExclusive, rbsInclusive, rbsInfinite); + + TPSQLRangeBoundValue = record + case Integer of + 0: (IVal: Integer); + 1: (FVal: Double); + 2: (DVal: TDateTime); + 3: (LVal: Int64); + //4: (TVal: TSQLTimeStamp); for future use + end; + PPSQLRangeBoundValue = ^TPSQLRangeBoundValue; + + TPSQLRangeBound = packed record + strict private + var Value: TPSQLRangeBoundValue; + public + State: TPSQLRangeBoundState; + property AsInteger: integer read Value.IVal write Value.IVal; + property AsFloat: double read Value.FVal write Value.FVal; + property AsLargeInt: Int64 read Value.LVal write Value.LVal; + property AsDateTime: TDateTime read Value.DVal write Value.DVal; + //property AsSQLTimeStamp: TSQLTimeStamp read Value.TVal write Value.TVal; for future use + class operator Equal(RB1: TPSQLRangeBound; RB2: TPSQLRangeBound): Boolean; + end; + + TPSQLRange = packed record + private + function GetEmpty: boolean; + public + LowerBound: TPSQLRangeBound; + UpperBound: TPSQLRangeBound; + property Empty: boolean read GetEmpty; + procedure SetEmpty; + class operator Equal(R1: TPSQLRange; R2: TPSQLRange): Boolean; + constructor Create(const Value: string; const RangeType: cardinal); + end; + + TPSQLPoint = packed record + X: Double; + Y: Double; + class operator Equal(P1: TPSQLPoint; P2: TPSQLPoint): Boolean; + class operator Implicit(P: TPoint): TPSQLPoint; + end; + + TPSQLCircle = packed record + R: Double; + class operator Equal(C1: TPSQLCircle; C2: TPSQLCircle): Boolean; + case Integer of + 0: (X, Y: Double); + 1: (Center: TPSQLPoint); + end; + + TPSQLBox = packed record + class operator Equal(B1: TPSQLBox; B2: TPSQLBox): Boolean; + case Integer of + 0: (Right, Top, Left, Bottom: Double); + 1: (TopRight, BottomLeft: TPSQLPoint); + end; + + TPSQLLSeg = packed record + class operator Equal(L1: TPSQLLSeg; L2: TPSQLLSeg): Boolean; + case Integer of + 0: (X1, Y1, X2, Y2: Double); + 1: (P1, P2: TPSQLPoint); + end; + +function SQLPointToPoint(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLPoint; +function PointToSQLPoint(Value: TPSQLPoint; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +function SQLCircleToCircle(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLCircle; +function CircleToSQLCircle(Value: TPSQLCircle; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +function SQLBoxToBox(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLBox; +function BoxToSQLBox(Value: TPSQLBox; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +function SQLLSegToLSeg(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLLSeg; +function LSegToSQLLSeg(Value: TPSQLLSeg; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +function SQLRangeToRange(Value: string; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLRange; +function RangeToSQLRange(Value: TPSQLRange; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): string; +{$ENDIF DELPHI_12} + +implementation + +{$IFDEF DELPHI_12} +uses PSQLAccess, PSQLTypes, SysUtils, StrUtils, Math; + +function SQLPointToPoint(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLPoint; +var S, Xs, Ys: string; + DelimPos: integer; +begin + S := Copy(Value, 2, Length(Value) - 2); //eliminate brackets + DelimPos := Pos(Delimiter, S); + Xs := Copy(S, 1, DelimPos - 1); + Ys := Copy(S, DelimPos + 1, MaxInt); + if not UseSystemSeparator then + begin + Result.X := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Xs, PSQL_FS); + Result.Y := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Ys, PSQL_FS); + end + else + begin + Result.X := {$IFDEF UNDER_DELPHI_6}SysUtils.{$ENDIF}StrToFloat(Xs); + Result.Y := {$IFDEF UNDER_DELPHI_6}SysUtils.{$ENDIF}StrToFloat(Ys); + end; +end; + +function PointToSQLPoint(Value: TPSQLPoint; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +begin + if UseSystemSeparator then + Result := Format('(%g' + Delimiter +'%g)', [Value.X, Value.Y]) + else + Result := '(' + SQLFloatToStr(Value.X) + Delimiter + SQLFloatToStr(Value.Y) + ')'; +end; + +function SQLCircleToCircle(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLCircle; +var S, Rs: string; + DelimPos: integer; +begin + S := Copy(Value, 2, Length(Value) - 2); //eliminate <> brackets + DelimPos := Pos(')', S); + Result.Center := SQLPointToPoint(Copy(S, 1, DelimPos), Delimiter, UseSystemSeparator); + Rs := Copy(S, DelimPos + 2, MaxInt); //closing bracket plus delimiter + if not UseSystemSeparator then + Result.R := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Rs, PSQL_FS) + else + Result.R := {$IFDEF UNDER_DELPHI_6}SysUtils.{$ENDIF}StrToFloat(Rs); +end; + +function CircleToSQLCircle(Value: TPSQLCircle; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +begin + Result := PointToSQLPoint(Value.Center, Delimiter, UseSystemSeparator); + with Value do + if UseSystemSeparator then + Result := '<' + Result + Delimiter + Format('%g>', [R]) + else + Result := '<' + Result + Delimiter + SQLFloatToStr(R) + '>'; +end; + +function SQLBoxToBox(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLBox; +var DelimPos: integer; +begin + DelimPos := Pos(')', Value) + 1; + Result.TopRight := SQLPointToPoint(Copy(Value, 1, DelimPos - 1), Delimiter, UseSystemSeparator); + Result.BottomLeft := SQLPointToPoint(Copy(Value, DelimPos + 1, MaxInt), Delimiter, UseSystemSeparator); +end; + +function BoxToSQLBox(Value: TPSQLBox; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +begin + Result := PointToSQLPoint(Value.TopRight, Delimiter, UseSystemSeparator) + Delimiter + PointToSQLPoint(Value.BottomLeft, Delimiter, UseSystemSeparator); +end; + +function SQLLSegToLSeg(Value: string; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLLSeg; +var + DelimPos: integer; + S: string; +begin + S := Copy(Value, 2, Length(Value) - 2); //eliminate [] brackets + DelimPos := Pos(')', S) + 1; + Result.P1 := SQLPointToPoint(Copy(S, 1, DelimPos - 1), Delimiter, UseSystemSeparator); + Result.P2 := SQLPointToPoint(Copy(S, DelimPos + 1, MaxInt), Delimiter, UseSystemSeparator); +end; + +function LSegToSQLLSeg(Value: TPSQLLSeg; const Delimiter: char = ','; const UseSystemSeparator: boolean = False) : string; +begin + Result := '[' + PointToSQLPoint(Value.P1, Delimiter, UseSystemSeparator) + Delimiter + PointToSQLPoint(Value.P2, Delimiter, UseSystemSeparator) + ']'; +end; + +function SQLRangeToRange(Value: string; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): TPSQLRange; +var + DelimPos: integer; + + procedure SetBound(var B: TPSQLRangeBound; SVal: string); + begin + if SVal = EmptyStr then + B.State := rbsInfinite + else + case RangeType of + FIELD_TYPE_NUMRANGE: if not UseSystemSeparator then + B.AsFloat := StrToFloat(SVal, PSQL_FS) + else + B.AsFloat := StrToFloat(SVal); + FIELD_TYPE_INT4RANGE: B.AsInteger := StrToInt(SVal); + FIELD_TYPE_INT8RANGE: B.AsLargeInt := StrToInt64(SVal); + FIELD_TYPE_DATERANGE: if UseSystemSeparator then + B.AsDateTime := StrToDate(SVal) + else + B.AsDateTime := SqlDateToDateTime(SVal, False); + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: if UseSystemSeparator then + B.AsDateTime := StrToDateTime(SVal) + else + B.AsDateTime := SQLTimeStampToDateTime(SVal); + end; + end; + +begin + Result := Default(TPSQLRange); + if SameText(Value, 'empty') or (Length(Value) <= 2) then + Result.SetEmpty + else + begin + Result.LowerBound.State := TPSQLRangeBoundState(ifthen(Value[START_STR_INDEX] = '[', ord(rbsInclusive), ord(rbsExclusive))); + Result.UpperBound.State := TPSQLRangeBoundState(ifthen(Value[Length(Value){$IFDEF ZEROBASEDSTRINGS}-1{$ENDIF}] = ']', ord(rbsInclusive), ord(rbsExclusive))); + Value := Copy(Value, 2, Length(Value) - 2); //eliminate brackets + DelimPos := Pos(Delimiter, Value); + SetBound(Result.LowerBound, AnsiDequotedStr(Copy(Value, 1, DelimPos - 1), '"')); // eliminate quotes if needed + SetBound(Result.UpperBound, AnsiDequotedStr(Copy(Value, DelimPos + 1, MaxInt), '"')); + end; +end; + +function RangeToSQLRange(Value: TPSQLRange; const RangeType: cardinal; const Delimiter: char = ','; const UseSystemSeparator: boolean = False): string; + + function GetBound(const B: TPSQLRangeBound): string; + begin + case RangeType of + FIELD_TYPE_INT4RANGE: Result := IntToStr(B.AsInteger); + FIELD_TYPE_INT8RANGE: Result := IntToStr(B.AsLargeInt); + FIELD_TYPE_NUMRANGE: if UseSystemSeparator then + Result := FloatToStr(B.AsFloat) + else + Result := SQLFloatToStr(B.AsFloat); + FIELD_TYPE_DATERANGE: if UseSystemSeparator then + Result := DateToStr(B.AsDateTime) + else + Result := DateTimeToSqlDate(B.AsDateTime, DATE_MODE); + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: if UseSystemSeparator then + Result := DateTimeToStr(B.AsDateTime) + else + Result := DateTimeToSqlDate(B.AsDateTime, TIMESTAMP_MODE); + end; + end; + +begin + if Value.Empty then Exit('empty'); + if Value.LowerBound.State = rbsInfinite then + Result := '[' + else + begin + Result := ifthen(Value.LowerBound.State = rbsInclusive, '[', '('); + Result := Result + GetBound(Value.LowerBound); + end; + Result := Result + Delimiter; + if Value.UpperBound.State = rbsInfinite then + Result := Result + ']' + else + begin + Result := Result + GetBound(Value.UpperBound); + Result := Result + ifthen(Value.UpperBound.State = rbsInclusive, ']', ')'); + end; +end; + +{ TPSQLRange } + +class operator TPSQLRangeBound.Equal(RB1: TPSQLRangeBound; RB2: TPSQLRangeBound): Boolean; +begin + Result := (RB1.State = RB2.State) + and CompareMem(@RB1.Value, @RB2.Value, SizeOf(TPSQLRangeBoundValue)); //timestamp is the biggest field in record +end; + +constructor TPSQLRange.Create(const Value: string; const RangeType: cardinal); +begin + case RangeType of + FIELD_TYPE_DATERANGE, + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: Self := SQLRangeToRange(Value, RangeType); + else + raise EConvertError.Create(SInvalidRangeType); + end; +end; + +class operator TPSQLRange.Equal(R1: TPSQLRange; R2: TPSQLRange): Boolean; +begin + Result := (R1.Empty = R2.Empty) and (R1.LowerBound = R2.LowerBound) and (R1.UpperBound = R2.UpperBound); +end; + +function TPSQLRange.GetEmpty: boolean; +begin + Result := (LowerBound.State = rbsExclusive) and (LowerBound = UpperBound); +end; + +{ TPSQLPoint } + +class operator TPSQLPoint.Equal(P1, P2: TPSQLPoint): Boolean; +begin + Result := SameValue(P1.X, P2.X, 0) and SameValue(P1.Y, P2.Y, 0); +end; + +class operator TPSQLPoint.Implicit(P: TPoint): TPSQLPoint; +begin + Result.X := P.X; + Result.Y := P.Y; +end; + +{ TPSQLCircle } + +class operator TPSQLCircle.Equal(C1: TPSQLCircle; C2: TPSQLCircle): Boolean; +begin + Result := (C1.Center = C2.Center) and SameValue(C1.R, C2.R); +end; + +{ TPSQLBox } + +class operator TPSQLBox.Equal(B1, B2: TPSQLBox): Boolean; +begin + Result := (B1.TopRight = B2.TopRight) and (B1.BottomLeft = B2.BottomLeft); +end; + +{ TPSQLLSeg } + +class operator TPSQLLSeg.Equal(L1: TPSQLLSeg; L2: TPSQLLSeg): Boolean; +begin + Result := (L1.P1 = L2.P1) and (L1.P2 = L2.P2); +end; + +{TPSQLRange} + +procedure TPSQLRange.SetEmpty; +begin + UpperBound.State := rbsExclusive; + LowerBound.State := rbsExclusive; +// UpperBound.AsSQLTimeStamp := NullSQLTimeStamp; +// LowerBound.AsSQLTimeStamp := NullSQLTimeStamp; +end; + +{$ENDIF DELPHI_12} + + +end. diff --git a/PSQLMacroQuery.pas b/Source/PSQLMacroQuery.pas similarity index 95% rename from PSQLMacroQuery.pas rename to Source/PSQLMacroQuery.pas index 3a7b0c7..2e540d1 100644 --- a/PSQLMacroQuery.pas +++ b/Source/PSQLMacroQuery.pas @@ -1,483 +1,483 @@ -{$I pSQLDAC.inc} -unit PSQLMacroQuery; - -{SVN revision: $Id$} - -{$P+,W-,R-} - -interface - -uses {$IFDEF FPC}LCLIntf,{$ENDIF} Classes, SysUtils, DB, PSQLDbTables, PSQLTypes, PSQLAccess; - -const - DefaultMacroChar = '%'; - TrueExpr = '0=0'; - -type -{$IFNDEF FPC} - {$IFNDEF NEXTGEN} - TCharSet = TSysCharSet; - {$ELSE} - TCharSet = array of Char; - {$ENDIF} -{$ENDIF} - -{ TPSQLMacroQuery } - TPSQLMacroQuery = class(TPSQLQuery) - private - FDisconnectExpected: Boolean; - FSaveQueryChanged: TNotifyEvent; - FMacroChar: Char; - FMacros: TParams; - FSQLPattern: TStrings; - FStreamPatternChanged: Boolean; - FPatternChanged: Boolean; - function GetMacros: TParams; - procedure SetMacros(Value: TParams); - procedure SetSQL(Value: TStrings); - procedure PatternChanged(Sender: TObject); - procedure QueryChanged(Sender: TObject); - procedure RecreateMacros; - procedure CreateMacros(List: TParams; const Value: PChar); - procedure Expand(Query: TStrings); - function GetMacroCount: Word; - procedure SetMacroChar(Value: Char); - protected - procedure Loaded; override; - function CreateHandle: HDBICur; override; - procedure OpenCursor(InfoQuery: Boolean); override; - procedure Disconnect; override; - protected - { IProviderSupport } - {$IFNDEF FPC} - procedure PSExecute; override; - function PSGetDefaultOrder: TIndexDef; override; - function PSGetTableName: string; override; - {$ENDIF} - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - procedure ExpandMacros; - procedure ExecSQL; - procedure Prepare; - procedure ExecDirect; - function MacroByName(const Value: string): TParam; - function IsEmpty: Boolean; - procedure Reopen; - property MacroCount: Word read GetMacroCount; - published - property MacroChar: Char read FMacroChar write SetMacroChar default DefaultMacroChar; - property SQL: TStrings read FSQLPattern write SetSQL; - property Macros: TParams read GetMacros write SetMacros; - end; - -(*{ TMacroQueryThread } - TRunQueryMode = (rqOpen, rqExecute, rqExecDirect); - - TPSQLMacroQueryThread = class(TThread) - private - FData: TPSQLDataSet; - FMode: TRunQueryMode; - FPrepare: Boolean; - FException: TObject; - procedure DoHandleException; - protected - procedure ModeError; virtual; - procedure DoTerminate; override; - procedure Execute; override; - procedure HandleException; virtual; - public - constructor Create(Data: TPSQLDataSet; RunMode: TRunQueryMode; - Prepare, CreateSuspended: Boolean); - end;*) - -procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean; - SpecialChar: Char; Delims: TCharSet); -function IsDataSetEmpty(DataSet: TDataSet): Boolean; - - -implementation - -{$IFDEF NEXTGEN} -uses Character; -{$ENDIF} - -{ Parse SQL utility routines } -function NameDelimiter(C: Char; Delims: TCharSet): Boolean; -begin -{$IFNDEF NEXTGEN} - Result := CharInSet(C, [' ', ',', ';', ')', #13, #10] + Delims); -{$ELSE} - Result := C.IsInArray([' ', ',', ';', ')', #13, #10]) or C.IsInArray(Delims); -{$ENDIF} -end; - -function IsLiteral(C: Char): Boolean; -begin -{$IFNDEF NEXTGEN} - Result := CharInSet(C, ['''', '"']); -{$ELSE} - Result := C.IsInArray(['''', '"']); -{$ENDIF} -end; - -procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean; - SpecialChar: Char; Delims: TCharSet); -var - CurPos, StartPos: PChar; - CurChar: Char; - Literal: Boolean; - EmbeddedLiteral: Boolean; - Name: string; - - function StripLiterals(Buffer: PChar): string; - var - Len: Word; - TempBuf: PChar; - - procedure StripChar(Value: Char); - begin - if TempBuf^ = Value then - StrMove(TempBuf, TempBuf + 1, Len - 1); - if TempBuf[StrLen(TempBuf) - 1] = Value then - TempBuf[StrLen(TempBuf) - 1] := #0; - end; - - begin - Len := StrLen(Buffer) + 1; - TempBuf := AllocMem(Len); - Result := ''; - try - StrCopy(TempBuf, Buffer); - StripChar(''''); - StripChar('"'); - Result := StrPas(TempBuf); - finally - FreeMem(TempBuf, Len); - end; - end; - -begin - if SpecialChar = #0 then Exit; - CurPos := Value; - Literal := False; - EmbeddedLiteral := False; - repeat - CurChar := CurPos^; - if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ <> SpecialChar) then - begin - StartPos := CurPos; - while (CurChar <> #0) and (Literal or not NameDelimiter(CurChar, Delims)) do begin - Inc(CurPos); - CurChar := CurPos^; - if IsLiteral(CurChar) then - begin - Literal := Literal xor True; - if CurPos = StartPos + 1 then EmbeddedLiteral := True; - end; - end; - CurPos^ := #0; - if EmbeddedLiteral then - begin - Name := StripLiterals(StartPos + 1); - EmbeddedLiteral := False; - end - else Name := StrPas(StartPos + 1); - if Assigned(List) then - begin - if Macro then - List.CreateParam(ftString, Name, ptInput).AsString := TrueExpr - else List.CreateParam(ftUnknown, Name, ptUnknown); - end; - CurPos^ := CurChar; - StartPos^ := '?'; - Inc(StartPos); - StrMove(StartPos, CurPos, StrLen(CurPos) + 1); - CurPos := StartPos; - end - else if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ = SpecialChar) then - StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1) - else if IsLiteral(CurChar) then Literal := Literal xor True; - Inc(CurPos); - until CurChar = #0; -end; - -function IsDataSetEmpty(DataSet: TDataSet): Boolean; -begin - with DataSet do Result := (not Active) or (Eof and Bof); -end; - - -{ TPSQLMacroQuery } -constructor TPSQLMacroQuery.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FSaveQueryChanged := TStringList(inherited SQL).OnChange; - TStringList(inherited SQL).OnChange := QueryChanged; - FMacroChar := DefaultMacroChar; - FSQLPattern := TStringList.Create; - TStringList(SQL).OnChange := PatternChanged; - FMacros := TParams.Create(Self); -end; - -destructor TPSQLMacroQuery.Destroy; -begin - Destroying; - Disconnect; - FMacros.Free; - FSQLPattern.Free; - inherited Destroy; -end; - -procedure TPSQLMacroQuery.Loaded; -begin - inherited Loaded; - GetMacros; -end; - -function TPSQLMacroQuery.CreateHandle: HDBICur; -begin - Result := inherited CreateHandle; -end; - -procedure TPSQLMacroQuery.OpenCursor; -begin - ExpandMacros; - inherited OpenCursor(InfoQuery); -end; - -procedure TPSQLMacroQuery.ExecSQL; -begin - ExpandMacros; - inherited ExecSQL; -end; - -procedure TPSQLMacroQuery.Prepare; -begin - ExpandMacros; - inherited Prepare; -end; - - -procedure TPSQLMacroQuery.ExecDirect; -var - AffectedRows : Integer; -begin - CheckInactive; - SetDBFlag(dbfExecSQL, True); - try - if SQL.Count > 0 then - begin - PSQLDBTables.Check(Engine,Engine.QExecDirect(DBHandle, inherited SQL.Text, nil, AffectedRows)); - end - else DatabaseError(SEmptySQLStatement); - finally - SetDBFlag(dbfExecSQL, False); - end; -end; - -procedure TPSQLMacroQuery.Disconnect; -var - Strings: TStrings; - Event1, Event2: TNotifyEvent; -begin - inherited Disconnect; - if (csDestroying in ComponentState) then Exit; - Strings := inherited SQL; - Event1 := TStringList(Strings).OnChange; - Event2 := QueryChanged; - if @Event1 <> @Event2 then begin - if not FDisconnectExpected then SQL := inherited SQL; - TStringList(inherited SQL).OnChange := QueryChanged; - end; -end; - -procedure TPSQLMacroQuery.SetMacroChar(Value: Char); -begin - if Value <> FMacroChar then begin - FMacroChar := Value; - RecreateMacros; - end; -end; - -function TPSQLMacroQuery.GetMacros: TParams; -begin - if FStreamPatternChanged then - begin - FStreamPatternChanged := False; - PatternChanged(nil); - end; - Result := FMacros; -end; - -procedure TPSQLMacroQuery.SetMacros(Value: TParams); -begin - FMacros.AssignValues(Value); -end; - -procedure TPSQLMacroQuery.SetSQL(Value: TStrings); -begin - inherited Disconnect; - TStringList(FSQLPattern).OnChange := nil; - FSQLPattern.Assign(Value); - TStringList(FSQLPattern).OnChange := PatternChanged; - PatternChanged(nil); -end; - -procedure TPSQLMacroQuery.PatternChanged(Sender: TObject); -begin - if (csLoading in ComponentState) then - begin - FStreamPatternChanged := True; - Exit; - end; - inherited Disconnect; - RecreateMacros; - FPatternChanged := True; - try - ExpandMacros; - finally - FPatternChanged := False; - end; -end; - -procedure TPSQLMacroQuery.QueryChanged(Sender: TObject); -begin - FSaveQueryChanged(Sender); - if not FDisconnectExpected then - begin - SQL := inherited SQL; - end; -end; - -procedure TPSQLMacroQuery.ExpandMacros; -var - ExpandedSQL: TStringList; -begin - if not FPatternChanged and not FStreamPatternChanged and - (MacroCount = 0) then Exit; - ExpandedSQL := TStringList.Create; - try - Expand(ExpandedSQL); - FDisconnectExpected := True; - try - inherited SQL := ExpandedSQL; - finally - FDisconnectExpected := False; - end; - finally - ExpandedSQL.Free; - end; -end; - -procedure TPSQLMacroQuery.RecreateMacros; -var - List: TParams; -begin - List := TParams.Create(Self); - try - CreateMacros(List, PChar(FSQLPattern.Text)); - List.AssignValues(FMacros); - FMacros.Free; - FMacros := List; - except - List.Free; - end; -end; - -procedure TPSQLMacroQuery.CreateMacros(List: TParams; const Value: PChar); -begin - CreateQueryParams(List, Value, True, MacroChar, ['.']); -end; - -procedure TPSQLMacroQuery.Expand(Query: TStrings); - - function ReplaceString(const S: string): string; - var - I, J, P, LiteralChars: Integer; - Param: TParam; - Found: Boolean; - begin - Result := S; - for I := Macros.Count - 1 downto 0 do begin - Param := Macros[I]; - if Param.DataType = ftUnknown then Continue; - repeat - P := Pos(MacroChar + Param.Name, Result); - Found := (P > 0) and ((Length(Result) = P + Length(Param.Name)) or - NameDelimiter(Result[P + Length(Param.Name) + 1], ['.'])); - if Found then begin - LiteralChars := 0; - for J := 1 to P - 1 do - if IsLiteral(Result[J]) then Inc(LiteralChars); - Found := LiteralChars mod 2 = 0; - if Found then begin - Result := Copy(Result, 1, P - 1) + Param.Text + Copy(Result, - P + Length(Param.Name) + 1, MaxInt); - end; - end; - until not Found; - end; - end; - -var - I: Integer; -begin - for I := 0 to FSQLPattern.Count - 1 do - Query.Add(ReplaceString(FSQLPattern[I])); -end; - -function TPSQLMacroQuery.GetMacroCount: Word; -begin - Result := FMacros.Count; -end; - -function TPSQLMacroQuery.MacroByName(const Value: string): TParam; -begin - Result := FMacros.ParamByName(Value); -end; - -function TPSQLMacroQuery.IsEmpty: Boolean; -begin - Result := IsDataSetEmpty(Self); -end; - -procedure TPSQLMacroQuery.Reopen; -begin - DisableControls; - try - Close; - Open; - finally - EnableControls; - end; -end; - -{ TPSQLMacroQuery.IProviderSupport } -{$IFNDEF FPC} -function TPSQLMacroQuery.PSGetDefaultOrder: TIndexDef; -begin - ExpandMacros; - Result := inherited PSGetDefaultOrder; -end; - -function TPSQLMacroQuery.PSGetTableName: string; -begin - ExpandMacros; - Result := inherited PSGetTableName; -end; - -procedure TPSQLMacroQuery.PSExecute; -begin - ExecSQL; -end; -{$ENDIF} - -end. - - - - - - - +{$I pSQLDAC.inc} +unit PSQLMacroQuery; + +{SVN revision: $Id$} + +{$P+,W-,R-} + +interface + +uses {$IFDEF FPC}LCLIntf,{$ENDIF} Classes, SysUtils, DB, PSQLDbTables, PSQLTypes, PSQLAccess; + +const + DefaultMacroChar = '%'; + TrueExpr = '0=0'; + +type +{$IFNDEF FPC} + {$IFNDEF NEXTGEN} + TCharSet = TSysCharSet; + {$ELSE} + TCharSet = array of Char; + {$ENDIF} +{$ENDIF} + +{ TPSQLMacroQuery } + TPSQLMacroQuery = class(TPSQLQuery) + private + FDisconnectExpected: Boolean; + FSaveQueryChanged: TNotifyEvent; + FMacroChar: Char; + FMacros: TParams; + FSQLPattern: TStrings; + FStreamPatternChanged: Boolean; + FPatternChanged: Boolean; + function GetMacros: TParams; + procedure SetMacros(Value: TParams); + procedure SetSQL(Value: TStrings); + procedure PatternChanged(Sender: TObject); + procedure QueryChanged(Sender: TObject); + procedure RecreateMacros; + procedure CreateMacros(List: TParams; const Value: PChar); + procedure Expand(Query: TStrings); + function GetMacroCount: Word; + procedure SetMacroChar(Value: Char); + protected + procedure Loaded; override; + function CreateHandle: HDBICur; override; + procedure OpenCursor(InfoQuery: Boolean); override; + procedure Disconnect; override; + protected + { IProviderSupport } + {$IFNDEF FPC} + procedure PSExecute; override; + function PSGetDefaultOrder: TIndexDef; override; + function PSGetTableName: string; override; + {$ENDIF} + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure ExpandMacros; + procedure ExecSQL; + procedure Prepare; + procedure ExecDirect; + function MacroByName(const Value: string): TParam; + function IsEmpty: Boolean; + procedure Reopen; + property MacroCount: Word read GetMacroCount; + published + property MacroChar: Char read FMacroChar write SetMacroChar default DefaultMacroChar; + property SQL: TStrings read FSQLPattern write SetSQL; + property Macros: TParams read GetMacros write SetMacros; + end; + +(*{ TMacroQueryThread } + TRunQueryMode = (rqOpen, rqExecute, rqExecDirect); + + TPSQLMacroQueryThread = class(TThread) + private + FData: TPSQLDataSet; + FMode: TRunQueryMode; + FPrepare: Boolean; + FException: TObject; + procedure DoHandleException; + protected + procedure ModeError; virtual; + procedure DoTerminate; override; + procedure Execute; override; + procedure HandleException; virtual; + public + constructor Create(Data: TPSQLDataSet; RunMode: TRunQueryMode; + Prepare, CreateSuspended: Boolean); + end;*) + +procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean; + SpecialChar: Char; Delims: TCharSet); +function IsDataSetEmpty(DataSet: TDataSet): Boolean; + + +implementation + +{$IFDEF NEXTGEN} +uses Character; +{$ENDIF} + +{ Parse SQL utility routines } +function NameDelimiter(C: Char; Delims: TCharSet): Boolean; +begin +{$IFNDEF NEXTGEN} + Result := CharInSet(C, [' ', ',', ';', ')', #13, #10] + Delims); +{$ELSE} + Result := C.IsInArray([' ', ',', ';', ')', #13, #10]) or C.IsInArray(Delims); +{$ENDIF} +end; + +function IsLiteral(C: Char): Boolean; +begin +{$IFNDEF NEXTGEN} + Result := CharInSet(C, ['''', '"']); +{$ELSE} + Result := C.IsInArray(['''', '"']); +{$ENDIF} +end; + +procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean; + SpecialChar: Char; Delims: TCharSet); +var + CurPos, StartPos: PChar; + CurChar: Char; + Literal: Boolean; + EmbeddedLiteral: Boolean; + Name: string; + + function StripLiterals(Buffer: PChar): string; + var + Len: Word; + TempBuf: PChar; + + procedure StripChar(Value: Char); + begin + if TempBuf^ = Value then + StrMove(TempBuf, TempBuf + 1, Len - 1); + if TempBuf[StrLen(TempBuf) - 1] = Value then + TempBuf[StrLen(TempBuf) - 1] := #0; + end; + + begin + Len := StrLen(Buffer) + 1; + TempBuf := AllocMem(Len); + Result := ''; + try + StrCopy(TempBuf, Buffer); + StripChar(''''); + StripChar('"'); + Result := StrPas(TempBuf); + finally + FreeMem(TempBuf, Len); + end; + end; + +begin + if SpecialChar = #0 then Exit; + CurPos := Value; + Literal := False; + EmbeddedLiteral := False; + repeat + CurChar := CurPos^; + if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ <> SpecialChar) then + begin + StartPos := CurPos; + while (CurChar <> #0) and (Literal or not NameDelimiter(CurChar, Delims)) do begin + Inc(CurPos); + CurChar := CurPos^; + if IsLiteral(CurChar) then + begin + Literal := Literal xor True; + if CurPos = StartPos + 1 then EmbeddedLiteral := True; + end; + end; + CurPos^ := #0; + if EmbeddedLiteral then + begin + Name := StripLiterals(StartPos + 1); + EmbeddedLiteral := False; + end + else Name := StrPas(StartPos + 1); + if Assigned(List) then + begin + if Macro then + List.CreateParam(ftString, Name, ptInput).AsString := TrueExpr + else List.CreateParam(ftUnknown, Name, ptUnknown); + end; + CurPos^ := CurChar; + StartPos^ := '?'; + Inc(StartPos); + StrMove(StartPos, CurPos, StrLen(CurPos) + 1); + CurPos := StartPos; + end + else if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ = SpecialChar) then + StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1) + else if IsLiteral(CurChar) then Literal := Literal xor True; + Inc(CurPos); + until CurChar = #0; +end; + +function IsDataSetEmpty(DataSet: TDataSet): Boolean; +begin + with DataSet do Result := (not Active) or (Eof and Bof); +end; + + +{ TPSQLMacroQuery } +constructor TPSQLMacroQuery.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FSaveQueryChanged := TStringList(inherited SQL).OnChange; + TStringList(inherited SQL).OnChange := QueryChanged; + FMacroChar := DefaultMacroChar; + FSQLPattern := TStringList.Create; + TStringList(SQL).OnChange := PatternChanged; + FMacros := TParams.Create(Self); +end; + +destructor TPSQLMacroQuery.Destroy; +begin + Destroying; + Disconnect; + FMacros.Free; + FSQLPattern.Free; + inherited Destroy; +end; + +procedure TPSQLMacroQuery.Loaded; +begin + inherited Loaded; + GetMacros; +end; + +function TPSQLMacroQuery.CreateHandle: HDBICur; +begin + Result := inherited CreateHandle; +end; + +procedure TPSQLMacroQuery.OpenCursor; +begin + ExpandMacros; + inherited OpenCursor(InfoQuery); +end; + +procedure TPSQLMacroQuery.ExecSQL; +begin + ExpandMacros; + inherited ExecSQL; +end; + +procedure TPSQLMacroQuery.Prepare; +begin + ExpandMacros; + inherited Prepare; +end; + + +procedure TPSQLMacroQuery.ExecDirect; +var + AffectedRows : Integer; +begin + CheckInactive; + SetDBFlag(dbfExecSQL, True); + try + if SQL.Count > 0 then + begin + PSQLDBTables.Check(Engine,Engine.QExecDirect(DBHandle, inherited SQL.Text, nil, AffectedRows)); + end + else DatabaseError(SEmptySQLStatement); + finally + SetDBFlag(dbfExecSQL, False); + end; +end; + +procedure TPSQLMacroQuery.Disconnect; +var + Strings: TStrings; + Event1, Event2: TNotifyEvent; +begin + inherited Disconnect; + if (csDestroying in ComponentState) then Exit; + Strings := inherited SQL; + Event1 := TStringList(Strings).OnChange; + Event2 := QueryChanged; + if @Event1 <> @Event2 then begin + if not FDisconnectExpected then SQL := inherited SQL; + TStringList(inherited SQL).OnChange := QueryChanged; + end; +end; + +procedure TPSQLMacroQuery.SetMacroChar(Value: Char); +begin + if Value <> FMacroChar then begin + FMacroChar := Value; + RecreateMacros; + end; +end; + +function TPSQLMacroQuery.GetMacros: TParams; +begin + if FStreamPatternChanged then + begin + FStreamPatternChanged := False; + PatternChanged(nil); + end; + Result := FMacros; +end; + +procedure TPSQLMacroQuery.SetMacros(Value: TParams); +begin + FMacros.AssignValues(Value); +end; + +procedure TPSQLMacroQuery.SetSQL(Value: TStrings); +begin + inherited Disconnect; + TStringList(FSQLPattern).OnChange := nil; + FSQLPattern.Assign(Value); + TStringList(FSQLPattern).OnChange := PatternChanged; + PatternChanged(nil); +end; + +procedure TPSQLMacroQuery.PatternChanged(Sender: TObject); +begin + if (csLoading in ComponentState) then + begin + FStreamPatternChanged := True; + Exit; + end; + inherited Disconnect; + RecreateMacros; + FPatternChanged := True; + try + ExpandMacros; + finally + FPatternChanged := False; + end; +end; + +procedure TPSQLMacroQuery.QueryChanged(Sender: TObject); +begin + FSaveQueryChanged(Sender); + if not FDisconnectExpected then + begin + SQL := inherited SQL; + end; +end; + +procedure TPSQLMacroQuery.ExpandMacros; +var + ExpandedSQL: TStringList; +begin + if not FPatternChanged and not FStreamPatternChanged and + (MacroCount = 0) then Exit; + ExpandedSQL := TStringList.Create; + try + Expand(ExpandedSQL); + FDisconnectExpected := True; + try + inherited SQL := ExpandedSQL; + finally + FDisconnectExpected := False; + end; + finally + ExpandedSQL.Free; + end; +end; + +procedure TPSQLMacroQuery.RecreateMacros; +var + List: TParams; +begin + List := TParams.Create(Self); + try + CreateMacros(List, PChar(FSQLPattern.Text)); + List.AssignValues(FMacros); + FMacros.Free; + FMacros := List; + except + List.Free; + end; +end; + +procedure TPSQLMacroQuery.CreateMacros(List: TParams; const Value: PChar); +begin + CreateQueryParams(List, Value, True, MacroChar, ['.']); +end; + +procedure TPSQLMacroQuery.Expand(Query: TStrings); + + function ReplaceString(const S: string): string; + var + I, J, P, LiteralChars: Integer; + Param: TParam; + Found: Boolean; + begin + Result := S; + for I := Macros.Count - 1 downto 0 do begin + Param := Macros[I]; + if Param.DataType = ftUnknown then Continue; + repeat + P := Pos(MacroChar + Param.Name, Result); + Found := (P > 0) and ((Length(Result) = P + Length(Param.Name)) or + NameDelimiter(Result[P + Length(Param.Name) + 1], ['.'])); + if Found then begin + LiteralChars := 0; + for J := 1 to P - 1 do + if IsLiteral(Result[J]) then Inc(LiteralChars); + Found := LiteralChars mod 2 = 0; + if Found then begin + Result := Copy(Result, 1, P - 1) + Param.Text + Copy(Result, + P + Length(Param.Name) + 1, MaxInt); + end; + end; + until not Found; + end; + end; + +var + I: Integer; +begin + for I := 0 to FSQLPattern.Count - 1 do + Query.Add(ReplaceString(FSQLPattern[I])); +end; + +function TPSQLMacroQuery.GetMacroCount: Word; +begin + Result := FMacros.Count; +end; + +function TPSQLMacroQuery.MacroByName(const Value: string): TParam; +begin + Result := FMacros.ParamByName(Value); +end; + +function TPSQLMacroQuery.IsEmpty: Boolean; +begin + Result := IsDataSetEmpty(Self); +end; + +procedure TPSQLMacroQuery.Reopen; +begin + DisableControls; + try + Close; + Open; + finally + EnableControls; + end; +end; + +{ TPSQLMacroQuery.IProviderSupport } +{$IFNDEF FPC} +function TPSQLMacroQuery.PSGetDefaultOrder: TIndexDef; +begin + ExpandMacros; + Result := inherited PSGetDefaultOrder; +end; + +function TPSQLMacroQuery.PSGetTableName: string; +begin + ExpandMacros; + Result := inherited PSGetTableName; +end; + +procedure TPSQLMacroQuery.PSExecute; +begin + ExecSQL; +end; +{$ENDIF} + +end. + + + + + + + diff --git a/PSQLMigrator.pas b/Source/PSQLMigrator.pas similarity index 97% rename from PSQLMigrator.pas rename to Source/PSQLMigrator.pas index a588aea..ba57d0b 100644 --- a/PSQLMigrator.pas +++ b/Source/PSQLMigrator.pas @@ -1,603 +1,603 @@ -{$I Psqldac.inc} -unit PSQLMigrator -{$IFDEF DELPHI_15} deprecated 'Will be removed from suite shortly! Consider using reFind.exe, see help for details'{$ENDIF}; - -{SVN revision: $Id$} - -interface -uses Classes, DB, PSQLDBTables, PSQLMacroQuery, Forms, SysUtils, PSQLTypes; - -type - - PDataSetPair = ^TDataSetPair; - TDataSetPair = record - OldDataSet, - NewDataSet: TDataSet; - end; - - TConvertComponent = (convBDE, convADO, convDBX, convZeos, convMySQLDAC); - TConvertComponents = set of TConvertComponent; - - - TDataSetList = class(TList) - private - function GetDataSetPairs(Index: Integer): PDataSetPair; - procedure SetDataSetPairs(Index: Integer; Item: PDataSetPair); - function GetOldDataSets(Index: Integer): TDataSet; - procedure SetOldDataSets(Index: Integer; Item: TDataSet); - function GetNewDataSets(Index: Integer): TDataSet; - procedure SetNewDataSets(Index: Integer; Item: TDataSet); - public - function GetPaired(aDataSet: TDataSet): TDataSet; - function IndexOfOldDataSet(aDataSet: TDataSet): Integer; - procedure DeletePair(Index: Integer); - procedure ClearAll; - destructor Destroy; override; - property Pairs[Index: Integer]: PDataSetPair read GetDataSetPairs write SetDataSetPairs; - property OldDataSets[Index: Integer]: TDataSet read GetOldDataSets write SetOldDataSets; - property NewDataSets[Index: Integer]: TDataSet read GetNewDataSets write SetNewDataSets; - end; - - TBDE2PSQLDAC = class(TComponent) - private - FAbout : TPSQLDACAbout; - FPSQLDatabase: TPSQLDatabase; - FDataSets: TDataSetList; - FFields: TList; - FExecute: Boolean; - FDeleteSourceComponents: Boolean; - FConvertComponents: TConvertComponents; - FSourceConnection: TCustomConnection; - function GetDatabase: TPSQLDatabase; - procedure SetDatabase(Value: TPSQLDatabase); - procedure FillComponents(ACompList: TList; ACompClass: TClass); - procedure SetDeleteSourceComponents(const Value: Boolean); - procedure SetConvertComponents(const Value: TConvertComponents); - procedure SetSourceConnection(const Value: TCustomConnection); - protected - { Common methods } - procedure Notification(AComponent: TComponent; Operation: TOperation); override; - procedure MigrateTables; - procedure MigrateDataSources; - procedure MigrateLookupFields; - procedure UpdateDesignInfo(OldDataSet, NewComponent: TComponent); - procedure AssignEvents(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - procedure AssignFields(OldField, NewField: TField); - procedure NewDataSetPair(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - procedure MoveFields(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - procedure CreatePSQLDataSet(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - procedure CheckDataSet(OldDataSet: TDataSet); - procedure GetNeededSQLs(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - procedure GetCachedUpdates(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - procedure GetNeededUpdateObject(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); - public - procedure Migrate; virtual; - constructor Create(aOwner: TComponent); override; - destructor Destroy; override; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property Database: TPSQLDatabase read GetDatabase write SetDatabase; - property SourceConnection: TCustomConnection read FSourceConnection write SetSourceConnection; - property Execute: Boolean read FExecute write FExecute; - property ConvertComponents: TConvertComponents read FConvertComponents write SetConvertComponents; - property DeleteSourceComponents: Boolean read FDeleteSourceComponents - write SetDeleteSourceComponents default True; - end; - -{$IFDEF DELPHI_5} -const - S_OK = 0; -{$ENDIF} - - -implementation - -uses Dialogs, Controls, ToolsAPI, TypInfo; - -{ TDataSetList } -function TDataSetList.GetDataSetPairs(Index: Integer): PDataSetPair; -begin - Result := PDataSetPair(Items[Index]); -end; - -function TDataSetList.GetNewDataSets(Index: Integer): TDataSet; -begin - Result := Pairs[Index].NewDataSet; -end; - -function TDataSetList.GetOldDataSets(Index: Integer): TDataSet; -begin - Result := Pairs[Index].OldDataSet; -end; - -procedure TDataSetList.SetDataSetPairs(Index: Integer; Item: PDataSetPair); -begin - Items[Index] := Item; -end; - -procedure TDataSetList.SetNewDataSets(Index: Integer; Item: TDataSet); -begin - Pairs[Index].NewDataSet := Item; -end; - -procedure TDataSetList.SetOldDataSets(Index: Integer; Item: TDataSet); -begin - Pairs[Index].OldDataSet := Item; -end; - -function TDataSetList.GetPaired(aDataSet: TDataSet): TDataSet; -var - I: Integer; -begin - Result := nil; - for I := 0 to Count-1 do - if OldDataSets[I] = aDataSet then - begin - Result := NewDataSets[I]; - exit; - end; -end; - -function TDataSetList.IndexOfOldDataSet(aDataSet: TDataSet): Integer; -var Index: Integer; -begin - Result := -1; - for Index := 0 to pred(Count) do - if OldDataSets[Index] = aDataSet then begin - Result := Index; - exit; - end; -end; - -procedure TDataSetList.ClearAll; -var Index: Integer; -begin - for Index := pred(Count) downto 0 do DeletePair(Index); -end; - -procedure TDataSetList.DeletePair(Index: Integer); -begin - FreeMem(PDataSetPair(Items[Index])); - Delete(Index); -end; - -destructor TDataSetList.Destroy; -begin - ClearAll; - inherited; -end; - -{ TBDE2PSQLDAC } -function TBDE2PSQLDAC.GetDatabase: TPSQLDatabase; -begin - Result := FPSQLDatabase; -end; - -procedure TBDE2PSQLDAC.SetDatabase(Value: TPSQLDatabase); -begin - FPSQLDatabase := Value; - if Value <> nil then Value.FreeNotification(Self) -end; - - -procedure TBDE2PSQLDAC.Notification(AComponent: TComponent; Operation: TOperation); -begin - inherited Notification(AComponent, Operation); - if (Operation = opRemove) then - if (aComponent = FPSQLDatabase) then - FPSQLDatabase := nil - else - if (aComponent = FSourceConnection) then - FSourceConnection := nil; -end; - -procedure TBDE2PSQLDAC.AssignEvents(aDataSet: TPSQLDataSet; - OldDataSet: TDataSet); -begin - with OldDataSet do - begin - aDataSet.BeforeOpen := BeforeOpen; - aDataSet.AfterOpen := AfterOpen ; - aDataSet.BeforeClose := BeforeClose; - aDataSet.AfterClose := AfterClose; - aDataSet.BeforeInsert := BeforeInsert; - aDataSet.AfterInsert := AfterInsert; - aDataSet.BeforeEdit := BeforeEdit; - aDataSet.AfterEdit := AfterEdit; - aDataSet.BeforePost := BeforePost; - aDataSet.AfterPost := AfterPost; - aDataSet.BeforeCancel := BeforeCancel; - aDataSet.AfterCancel := AfterCancel; - aDataSet.BeforeDelete := BeforeDelete; - aDataSet.AfterDelete := AfterDelete; - aDataSet.BeforeScroll := BeforeScroll; - aDataSet.AfterScroll := AfterScroll; - aDataSet.OnCalcFields := OnCalcFields; - aDataSet.OnDeleteError := OnDeleteError; - aDataSet.OnEditError := OnEditError; - aDataSet.OnFilterRecord := OnFilterRecord; - aDataSet.OnNewRecord := OnNewRecord; - aDataSet.OnPostError := OnPostError; - end; -end; - -procedure TBDE2PSQLDAC.AssignFields(OldField, NewField: TField); -var - s: string; -begin - with OldField do - begin - NewField.FieldName := FieldName; - NewField.DisplayLabel := DisplayLabel; - NewField.FieldKind := FieldKind; - NewField.EditMask := EditMask; - NewField.Alignment := Alignment; - NewField.DefaultExpression := DefaultExpression; - NewField.DisplayWidth := DisplayWidth; - NewField.Visible := Visible; - NewField.KeyFields := KeyFields; - NewField.LookupCache := LookupCache; - NewField.LookupDataSet := FDataSets.GetPaired(LookupDataSet); - NewField.LookupKeyFields := LookupKeyFields; - NewField.LookupResultField := LookupResultField; - NewField.OnChange := OnChange; - NewField.OnGetText := OnGetText; - NewField.OnSetText := OnSetText; - NewField.OnValidate := OnValidate; - s := Name; - Name := 'Die_' + Name; - NewField.Name := s; - end -end; - -procedure TBDE2PSQLDAC.MoveFields(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); -begin - {$IFNDEF DELPHI_20}if OldDataSet.DefaultFields then Exit;{$ENDIF} - with OldDataSet do - while FieldCount > 0 do - Fields[0].DataSet := aDataSet; -end; - -procedure TBDE2PSQLDAC.UpdateDesignInfo(OldDataSet, NewComponent: TComponent); -begin - NewComponent.DesignInfo := OldDataSet.DesignInfo; - OldDataSet.Owner.RemoveComponent(NewComponent); - OldDataSet.Owner.InsertComponent(NewComponent); -end; - -procedure TBDE2PSQLDAC.CreatePSQLDataSet(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); -var - Index, NamePos: integer; - rName1, rName2: string; -begin - if Assigned(OldDataSet) then OldDataSet.Close; // PaGo 23.05.2007 - //UpdateDesignInfo(OldDataSet, aDataSet); - aDataSet.DataBase := FPSQLDatabase; - GetNeededSQLs(aDataSet, OldDataSet); - AssignEvents(aDataSet, OldDataSet); - MoveFields (aDataSet, OldDataSet); - GetCachedUpdates(aDataSet, OldDataSet); - OldDataSet.Name := 'Die_' + OldDataSet.Name; - aDataSet.Name := Copy(OldDataSet.Name, 5, length(OldDataSet.Name)); - for Index := 0 to pred(aDataSet.FieldCount) do - begin - rName1 := aDataSet.Fields[Index].Name; - NamePos := Pos(aDataSet.Name, rName1); - if NamePos = 1 then - begin - rName2 := Copy(rName1, length(aDataSet.Name) + 1, length(rName1)); - NamePos := Pos(aDataSet.Name, rName2); - if NamePos = 1 then aDataSet.Fields[Index].Name := rName2; - end; - end; - GetNeededUpdateObject(aDataSet, OldDataSet); -end; - -procedure TBDE2PSQLDAC.MigrateDataSources; -var - List: TList; - I: Integer; - DataSource: TDataSource; - DataSet: TDataSet; -begin - List := TList.Create; - try - FillComponents(List,TDataSource); - for I := 0 to List.Count-1 do - begin - DataSource := TDataSource(List[I]); - DataSet := nil; - if FDataSets.IndexOfOldDataSet(DataSource.DataSet) <> -1 then - DataSet := FDataSets.GetPaired(DataSource.DataSet); - if DataSet <> nil then - DataSource.DataSet := DataSet; - end; - finally - List.Free; - end; -end; - -procedure TBDE2PSQLDAC.Migrate; -var - I: Integer; -begin - if not Assigned(FPSQLDatabase) then - DatabaseError('Database not assigned',Self); - FDataSets.ClearAll; // PaGo 23.05.2007 - Screen.Cursor := crHourGlass; - try - MigrateTables; - MigrateDataSources; - MigrateLookupFields; - if DeleteSourceComponents then - for I:=0 to FDataSets.Count-1 do - begin - if GetPropInfo(FDataSets.OldDataSets[I], 'UpdateObject') <> nil then - GetObjectProp(FDataSets.OldDataSets[I], 'UpdateObject').Free; - FDataSets.OldDataSets[I].Free; - end; - ShowMessage(Format('%d dataset descendants processed successfully',[FDataSets.Count])); - finally - Screen.Cursor := crDefault; - end; -end; - -procedure TBDE2PSQLDAC.MigrateTables; -var - I: Integer; - List: TList; -begin - List := TList.Create; - try - FillComponents(List, TDataset); - for i := List.Count-1 downto 0 do - if SameText(TDataset(List[i]).ClassName, 'TNestedTable') then - List.Delete(I) - else - CheckDataset(TDataset(List[i])); - finally - List.Free; - end; - for I := 0 to FDataSets.Count - 1 do - CreatePSQLDataSet(TPSQLDataSet(FDataSets.NewDataSets[I]), FDataSets.OldDataSets[I]); -end; - -constructor TBDE2PSQLDAC.Create(aOwner: TComponent); -begin - inherited; - FDataSets := TDataSetList.Create; - FFields := TList.Create; - FDeleteSourceComponents := True; - FConvertComponents := [convBDE, convADO, convDBX, convZeos, convMySQLDAC]; -end; - -destructor TBDE2PSQLDAC.Destroy; -begin - FDataSets.Free; - FFields.Free; - inherited; -end; - -procedure TBDE2PSQLDAC.NewDataSetPair(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); -var - Pair: PDataSetPair; -begin - New(Pair); - Pair.OldDataSet := OldDataSet; - Pair.NewDataSet := aDataSet; - FDataSets.Add(Pair); -end; - -procedure TBDE2PSQLDAC.CheckDataSet(OldDataSet: TDataSet); -var - aDataSet: TPSQLDataSet; - anObj: TObject; - Propinfo: PPropInfo; -begin - if Assigned(FSourceConnection) then - begin - Propinfo := GetPropInfo(PTypeInfo(OldDataSet.ClassInfo), 'Connection'); //ADO & Zeos - if not Assigned(Propinfo) then - Propinfo := GetPropInfo(PTypeInfo(OldDataSet.ClassInfo), 'SQLConnection'); //dbX - if not Assigned(Propinfo) then - Propinfo := GetPropInfo(PTypeInfo(OldDataSet.ClassInfo), 'Database'); //MySqlDAC - if not Assigned(Propinfo) then Exit; - anObj := GetObjectProp(OldDataSet, Propinfo); - if anObj <> FSourceConnection then Exit; - end; - - aDataSet := nil; - - if OldDataSet.ClassNameIs('TQuery') and (convBDE in FConvertComponents) or - OldDataSet.ClassNameIs('TZQuery') and (convZeos in FConvertComponents) or - OldDataSet.ClassNameIs('TSQLQuery') and (convDBX in FConvertComponents) or - OldDataSet.ClassNameIs('TSQLDataset') and (convDBX in FConvertComponents) or - OldDataSet.ClassNameIs('TADOQuery') and (convADO in FConvertComponents) or - OldDataSet.ClassNameIs('TADODataset') and (convADO in FConvertComponents) or - OldDataSet.ClassNameIs('TMySQLQuery') and (convMySQLDAC in FConvertComponents) then - {$WARNINGS OFF} //make D5 happy - aDataSet := TPSQLQuery.Create(OldDataSet.Owner) - {$WARNINGS ON} //make D5 happy - - else - - if OldDataSet.ClassNameIs('TTable') and (convBDE in FConvertComponents) or - OldDataSet.ClassNameIs('TZTable') and (convZeos in FConvertComponents) or - OldDataSet.ClassNameIs('TSQLTable') and (convDBX in FConvertComponents) or - OldDataSet.ClassNameIs('TADOTable') and (convADO in FConvertComponents) or - OldDataSet.ClassNameIs('TMySQLTable') and (convMySQLDAC in FConvertComponents) then - {$WARNINGS OFF} //make D5 happy - aDataSet := TPSQLTable.Create(OldDataSet.Owner) - {$WARNINGS ON} //make D5 happy - - else - - if OldDataSet.ClassNameIs('TStoredProc') and (convBDE in FConvertComponents) or - OldDataSet.ClassNameIs('TZStoredProc') and (convZeos in FConvertComponents) or - OldDataSet.ClassNameIs('TSQLStoredProc') and (convDBX in FConvertComponents) or - OldDataSet.ClassNameIs('TADOStoredProc') and (convADO in FConvertComponents) or - OldDataSet.ClassNameIs('TMySQLStoredProc') and (convMySQLDAC in FConvertComponents)then - {$WARNINGS OFF} //make D5 happy - aDataSet := TPSQLStoredProc.Create(OldDataSet.Owner) - {$WARNINGS ON} //make D5 happy - else - - if OldDataSet.ClassNameIs('TMySQLMacroQuery') and (convMySQLDAC in FConvertComponents) then - {$WARNINGS OFF} //make D5 happy - aDataSet := TPSQLMacroQuery.Create(OldDataSet.Owner); - {$WARNINGS ON} //make D5 happy - - if Assigned(aDataset) then - begin - UpdateDesignInfo(OldDataSet, aDataSet); - NewDataSetPair(aDataSet, OldDataSet); - end; -end; - -procedure TBDE2PSQLDAC.GetCachedUpdates(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); -begin - aDataSet.CachedUpdates := False; -end; - -procedure TBDE2PSQLDAC.GetNeededSQLs(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); -var AObj: TObject; -begin - if OldDataSet.ClassNameIs('TQuery') or OldDataSet.ClassNameIs('TZQuery') or - OldDataSet.ClassNameIs('TSQLQuery') or OldDataSet.ClassNameIs('TADOQuery') or - OldDataSet.ClassNameIs('TMySQLMacroQuery') then - begin - AObj := GetObjectProp(OldDataSet,'SQL'); - if Assigned(AObj) and (AObj is TStrings) then - TPSQLQuery(aDataSet).SQL.Assign(AObj as TStrings) - end - else - if OldDataSet.ClassNameIs('TSQLDataset') or OldDataSet.ClassNameIs('TADODataset') then - TPSQLQuery(aDataSet).SQL.Text := GetStrProp(OldDataSet, 'CommandText') - else - if OldDataSet.ClassNameIs('TTable') or OldDataSet.ClassNameIs('TZTable') or - OldDataSet.ClassNameIs('TSQLTable') or OldDataSet.ClassNameIs('TADOTable') then - begin - TPSQLTable(aDataSet).TableName := GetStrProp(OldDataSet, 'TableName'); - if GetPropInfo(OldDataset,'Filtered') <> nil then - TPSQLTable(aDataSet).Filtered := Boolean(GetOrdProp(OldDataSet, 'Filtered')); - if GetPropInfo(OldDataset,'Filter') <> nil then - TPSQLTable(aDataSet).Filter := GetStrProp(OldDataSet, 'Filter'); - end - else - if OldDataSet.ClassNameIs('TStoredProc') or OldDataSet.ClassNameIs('TZStoredProc') or - OldDataSet.ClassNameIs('TSQLStoredProc') then - TPSQLStoredProc(aDataSet).StoredProcName := GetStrProp(OldDataSet, 'StoredProcName') - else - if OldDataSet.ClassNameIs('TADOStoredProc') then - TPSQLStoredProc(aDataSet).StoredProcName := GetStrProp(OldDataSet, 'ProcedureName'); -end; - -procedure TBDE2PSQLDAC.GetNeededUpdateObject(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); -var - Obj: TObject; - - procedure AssignSQL(const aSQL: TStrings; const PropName: string); - var AObj: TObject; - begin - try - AObj := GetObjectProp(Obj,PropName); - If Assigned(AObj) and (AObj is TStrings) then - aSQL.Assign(AObj as TStrings); - except - end; - end; - -begin - if GetPropInfo(OldDataset,'UpdateObject') <> nil then - Obj := GetObjectProp(OldDataSet, 'UpdateObject') - else - Exit; - if Assigned(Obj) and - (Obj.ClassNameIs('TUpdateSQL') or Obj.ClassNameIs('TZUpdateSQL')) then - begin - aDataset.UpdateObject := TPSQLUpdateSQL.Create((Obj as TComponent).Owner); - UpdateDesignInfo(Obj as TComponent,aDataset.UpdateObject); - (Obj as TComponent).Name := 'Die_' + (Obj as TComponent).Name; - aDataset.UpdateObject.Name := Copy((Obj as TComponent).Name,5,MaxInt); - AssignSQL(TPSQLUpdateSQL(aDataSet.UpdateObject).ModifySQL, 'ModifySQL'); - AssignSQL(TPSQLUpdateSQL(aDataSet.UpdateObject).InsertSQL, 'InsertSQL'); - AssignSQL(TPSQLUpdateSQL(aDataSet.UpdateObject).DeleteSQL, 'DeleteSQL'); - end; -end; - -procedure TBDE2PSQLDAC.FillComponents(ACompList: TList; - ACompClass: TClass); -var - I, J, K: Integer; - ModuleServices: IOTAModuleServices; - Module: IOTAModule; - Editor: IOTAEditor; - FormEditor: IOTAFormEditor; - RootComp: IOTAComponent; - Comp: TComponent; -begin - If not (Assigned(ACompList) and Assigned(ACompClass)) then - Exit; - - ModuleServices := BorlandIDEServices as IOTAModuleServices; - if ModuleServices = nil then Exit; - for I := 0 to ModuleServices.ModuleCount - 1 do - begin - Module := ModuleServices.Modules[I]; - for J := 0 to Module.GetModuleFileCount - 1 do - begin - Editor := Module.GetModuleFileEditor(J); - if Editor.QueryInterface(IOTAFormEditor, FormEditor) = S_OK then - begin - FormEditor.Show; - RootComp := FormEditor.GetRootComponent; - if RootComp <> nil then - begin - Comp := (RootComp as INTAComponent).GetComponent; - for K :=0 to Comp.ComponentCount-1 do - if Comp.Components[K] is ACompClass then - ACompList.Add(Comp.Components[K]); - end; - end; - end; - end; -end; - -procedure TBDE2PSQLDAC.SetDeleteSourceComponents(const Value: Boolean); -begin - FDeleteSourceComponents := Value; -end; - -procedure TBDE2PSQLDAC.SetSourceConnection(const Value: TCustomConnection); -begin - FSourceConnection := Value; - if Value <> nil then Value.FreeNotification(Self) -end; - -procedure TBDE2PSQLDAC.MigrateLookupFields; -var - FieldList: TList; - I: Integer; -begin - FieldList := TList.Create; - try - FillComponents(FieldList,TField); - for I := 0 to FieldList.Count-1 do - with TField(FieldList[i]) do - If Lookup then - LookupDataSet := FDataSets.GetPaired(LookupDataSet); - finally - FieldList.Free; - end; -end; - -procedure TBDE2PSQLDAC.SetConvertComponents( - const Value: TConvertComponents); -begin - FConvertComponents := Value; -end; - -end. - - - +{$I Psqldac.inc} +unit PSQLMigrator +{$IFDEF DELPHI_15} deprecated 'Will be removed from suite shortly! Consider using reFind.exe, see help for details'{$ENDIF}; + +{SVN revision: $Id$} + +interface +uses Classes, DB, PSQLDBTables, PSQLMacroQuery, Forms, SysUtils, PSQLTypes; + +type + + PDataSetPair = ^TDataSetPair; + TDataSetPair = record + OldDataSet, + NewDataSet: TDataSet; + end; + + TConvertComponent = (convBDE, convADO, convDBX, convZeos, convMySQLDAC); + TConvertComponents = set of TConvertComponent; + + + TDataSetList = class(TList) + private + function GetDataSetPairs(Index: Integer): PDataSetPair; + procedure SetDataSetPairs(Index: Integer; Item: PDataSetPair); + function GetOldDataSets(Index: Integer): TDataSet; + procedure SetOldDataSets(Index: Integer; Item: TDataSet); + function GetNewDataSets(Index: Integer): TDataSet; + procedure SetNewDataSets(Index: Integer; Item: TDataSet); + public + function GetPaired(aDataSet: TDataSet): TDataSet; + function IndexOfOldDataSet(aDataSet: TDataSet): Integer; + procedure DeletePair(Index: Integer); + procedure ClearAll; + destructor Destroy; override; + property Pairs[Index: Integer]: PDataSetPair read GetDataSetPairs write SetDataSetPairs; + property OldDataSets[Index: Integer]: TDataSet read GetOldDataSets write SetOldDataSets; + property NewDataSets[Index: Integer]: TDataSet read GetNewDataSets write SetNewDataSets; + end; + + TBDE2PSQLDAC = class(TComponent) + private + FAbout : TPSQLDACAbout; + FPSQLDatabase: TPSQLDatabase; + FDataSets: TDataSetList; + FFields: TList; + FExecute: Boolean; + FDeleteSourceComponents: Boolean; + FConvertComponents: TConvertComponents; + FSourceConnection: TCustomConnection; + function GetDatabase: TPSQLDatabase; + procedure SetDatabase(Value: TPSQLDatabase); + procedure FillComponents(ACompList: TList; ACompClass: TClass); + procedure SetDeleteSourceComponents(const Value: Boolean); + procedure SetConvertComponents(const Value: TConvertComponents); + procedure SetSourceConnection(const Value: TCustomConnection); + protected + { Common methods } + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure MigrateTables; + procedure MigrateDataSources; + procedure MigrateLookupFields; + procedure UpdateDesignInfo(OldDataSet, NewComponent: TComponent); + procedure AssignEvents(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + procedure AssignFields(OldField, NewField: TField); + procedure NewDataSetPair(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + procedure MoveFields(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + procedure CreatePSQLDataSet(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + procedure CheckDataSet(OldDataSet: TDataSet); + procedure GetNeededSQLs(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + procedure GetCachedUpdates(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + procedure GetNeededUpdateObject(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); + public + procedure Migrate; virtual; + constructor Create(aOwner: TComponent); override; + destructor Destroy; override; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property Database: TPSQLDatabase read GetDatabase write SetDatabase; + property SourceConnection: TCustomConnection read FSourceConnection write SetSourceConnection; + property Execute: Boolean read FExecute write FExecute; + property ConvertComponents: TConvertComponents read FConvertComponents write SetConvertComponents; + property DeleteSourceComponents: Boolean read FDeleteSourceComponents + write SetDeleteSourceComponents default True; + end; + +{$IFDEF DELPHI_5} +const + S_OK = 0; +{$ENDIF} + + +implementation + +uses Dialogs, Controls, ToolsAPI, TypInfo; + +{ TDataSetList } +function TDataSetList.GetDataSetPairs(Index: Integer): PDataSetPair; +begin + Result := PDataSetPair(Items[Index]); +end; + +function TDataSetList.GetNewDataSets(Index: Integer): TDataSet; +begin + Result := Pairs[Index].NewDataSet; +end; + +function TDataSetList.GetOldDataSets(Index: Integer): TDataSet; +begin + Result := Pairs[Index].OldDataSet; +end; + +procedure TDataSetList.SetDataSetPairs(Index: Integer; Item: PDataSetPair); +begin + Items[Index] := Item; +end; + +procedure TDataSetList.SetNewDataSets(Index: Integer; Item: TDataSet); +begin + Pairs[Index].NewDataSet := Item; +end; + +procedure TDataSetList.SetOldDataSets(Index: Integer; Item: TDataSet); +begin + Pairs[Index].OldDataSet := Item; +end; + +function TDataSetList.GetPaired(aDataSet: TDataSet): TDataSet; +var + I: Integer; +begin + Result := nil; + for I := 0 to Count-1 do + if OldDataSets[I] = aDataSet then + begin + Result := NewDataSets[I]; + exit; + end; +end; + +function TDataSetList.IndexOfOldDataSet(aDataSet: TDataSet): Integer; +var Index: Integer; +begin + Result := -1; + for Index := 0 to pred(Count) do + if OldDataSets[Index] = aDataSet then begin + Result := Index; + exit; + end; +end; + +procedure TDataSetList.ClearAll; +var Index: Integer; +begin + for Index := pred(Count) downto 0 do DeletePair(Index); +end; + +procedure TDataSetList.DeletePair(Index: Integer); +begin + FreeMem(PDataSetPair(Items[Index])); + Delete(Index); +end; + +destructor TDataSetList.Destroy; +begin + ClearAll; + inherited; +end; + +{ TBDE2PSQLDAC } +function TBDE2PSQLDAC.GetDatabase: TPSQLDatabase; +begin + Result := FPSQLDatabase; +end; + +procedure TBDE2PSQLDAC.SetDatabase(Value: TPSQLDatabase); +begin + FPSQLDatabase := Value; + if Value <> nil then Value.FreeNotification(Self) +end; + + +procedure TBDE2PSQLDAC.Notification(AComponent: TComponent; Operation: TOperation); +begin + inherited Notification(AComponent, Operation); + if (Operation = opRemove) then + if (aComponent = FPSQLDatabase) then + FPSQLDatabase := nil + else + if (aComponent = FSourceConnection) then + FSourceConnection := nil; +end; + +procedure TBDE2PSQLDAC.AssignEvents(aDataSet: TPSQLDataSet; + OldDataSet: TDataSet); +begin + with OldDataSet do + begin + aDataSet.BeforeOpen := BeforeOpen; + aDataSet.AfterOpen := AfterOpen ; + aDataSet.BeforeClose := BeforeClose; + aDataSet.AfterClose := AfterClose; + aDataSet.BeforeInsert := BeforeInsert; + aDataSet.AfterInsert := AfterInsert; + aDataSet.BeforeEdit := BeforeEdit; + aDataSet.AfterEdit := AfterEdit; + aDataSet.BeforePost := BeforePost; + aDataSet.AfterPost := AfterPost; + aDataSet.BeforeCancel := BeforeCancel; + aDataSet.AfterCancel := AfterCancel; + aDataSet.BeforeDelete := BeforeDelete; + aDataSet.AfterDelete := AfterDelete; + aDataSet.BeforeScroll := BeforeScroll; + aDataSet.AfterScroll := AfterScroll; + aDataSet.OnCalcFields := OnCalcFields; + aDataSet.OnDeleteError := OnDeleteError; + aDataSet.OnEditError := OnEditError; + aDataSet.OnFilterRecord := OnFilterRecord; + aDataSet.OnNewRecord := OnNewRecord; + aDataSet.OnPostError := OnPostError; + end; +end; + +procedure TBDE2PSQLDAC.AssignFields(OldField, NewField: TField); +var + s: string; +begin + with OldField do + begin + NewField.FieldName := FieldName; + NewField.DisplayLabel := DisplayLabel; + NewField.FieldKind := FieldKind; + NewField.EditMask := EditMask; + NewField.Alignment := Alignment; + NewField.DefaultExpression := DefaultExpression; + NewField.DisplayWidth := DisplayWidth; + NewField.Visible := Visible; + NewField.KeyFields := KeyFields; + NewField.LookupCache := LookupCache; + NewField.LookupDataSet := FDataSets.GetPaired(LookupDataSet); + NewField.LookupKeyFields := LookupKeyFields; + NewField.LookupResultField := LookupResultField; + NewField.OnChange := OnChange; + NewField.OnGetText := OnGetText; + NewField.OnSetText := OnSetText; + NewField.OnValidate := OnValidate; + s := Name; + Name := 'Die_' + Name; + NewField.Name := s; + end +end; + +procedure TBDE2PSQLDAC.MoveFields(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); +begin + {$IFNDEF DELPHI_20}if OldDataSet.DefaultFields then Exit;{$ENDIF} + with OldDataSet do + while FieldCount > 0 do + Fields[0].DataSet := aDataSet; +end; + +procedure TBDE2PSQLDAC.UpdateDesignInfo(OldDataSet, NewComponent: TComponent); +begin + NewComponent.DesignInfo := OldDataSet.DesignInfo; + OldDataSet.Owner.RemoveComponent(NewComponent); + OldDataSet.Owner.InsertComponent(NewComponent); +end; + +procedure TBDE2PSQLDAC.CreatePSQLDataSet(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); +var + Index, NamePos: integer; + rName1, rName2: string; +begin + if Assigned(OldDataSet) then OldDataSet.Close; // PaGo 23.05.2007 + //UpdateDesignInfo(OldDataSet, aDataSet); + aDataSet.DataBase := FPSQLDatabase; + GetNeededSQLs(aDataSet, OldDataSet); + AssignEvents(aDataSet, OldDataSet); + MoveFields (aDataSet, OldDataSet); + GetCachedUpdates(aDataSet, OldDataSet); + OldDataSet.Name := 'Die_' + OldDataSet.Name; + aDataSet.Name := Copy(OldDataSet.Name, 5, length(OldDataSet.Name)); + for Index := 0 to pred(aDataSet.FieldCount) do + begin + rName1 := aDataSet.Fields[Index].Name; + NamePos := Pos(aDataSet.Name, rName1); + if NamePos = 1 then + begin + rName2 := Copy(rName1, length(aDataSet.Name) + 1, length(rName1)); + NamePos := Pos(aDataSet.Name, rName2); + if NamePos = 1 then aDataSet.Fields[Index].Name := rName2; + end; + end; + GetNeededUpdateObject(aDataSet, OldDataSet); +end; + +procedure TBDE2PSQLDAC.MigrateDataSources; +var + List: TList; + I: Integer; + DataSource: TDataSource; + DataSet: TDataSet; +begin + List := TList.Create; + try + FillComponents(List,TDataSource); + for I := 0 to List.Count-1 do + begin + DataSource := TDataSource(List[I]); + DataSet := nil; + if FDataSets.IndexOfOldDataSet(DataSource.DataSet) <> -1 then + DataSet := FDataSets.GetPaired(DataSource.DataSet); + if DataSet <> nil then + DataSource.DataSet := DataSet; + end; + finally + List.Free; + end; +end; + +procedure TBDE2PSQLDAC.Migrate; +var + I: Integer; +begin + if not Assigned(FPSQLDatabase) then + DatabaseError('Database not assigned',Self); + FDataSets.ClearAll; // PaGo 23.05.2007 + Screen.Cursor := crHourGlass; + try + MigrateTables; + MigrateDataSources; + MigrateLookupFields; + if DeleteSourceComponents then + for I:=0 to FDataSets.Count-1 do + begin + if GetPropInfo(FDataSets.OldDataSets[I], 'UpdateObject') <> nil then + GetObjectProp(FDataSets.OldDataSets[I], 'UpdateObject').Free; + FDataSets.OldDataSets[I].Free; + end; + ShowMessage(Format('%d dataset descendants processed successfully',[FDataSets.Count])); + finally + Screen.Cursor := crDefault; + end; +end; + +procedure TBDE2PSQLDAC.MigrateTables; +var + I: Integer; + List: TList; +begin + List := TList.Create; + try + FillComponents(List, TDataset); + for i := List.Count-1 downto 0 do + if SameText(TDataset(List[i]).ClassName, 'TNestedTable') then + List.Delete(I) + else + CheckDataset(TDataset(List[i])); + finally + List.Free; + end; + for I := 0 to FDataSets.Count - 1 do + CreatePSQLDataSet(TPSQLDataSet(FDataSets.NewDataSets[I]), FDataSets.OldDataSets[I]); +end; + +constructor TBDE2PSQLDAC.Create(aOwner: TComponent); +begin + inherited; + FDataSets := TDataSetList.Create; + FFields := TList.Create; + FDeleteSourceComponents := True; + FConvertComponents := [convBDE, convADO, convDBX, convZeos, convMySQLDAC]; +end; + +destructor TBDE2PSQLDAC.Destroy; +begin + FDataSets.Free; + FFields.Free; + inherited; +end; + +procedure TBDE2PSQLDAC.NewDataSetPair(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); +var + Pair: PDataSetPair; +begin + New(Pair); + Pair.OldDataSet := OldDataSet; + Pair.NewDataSet := aDataSet; + FDataSets.Add(Pair); +end; + +procedure TBDE2PSQLDAC.CheckDataSet(OldDataSet: TDataSet); +var + aDataSet: TPSQLDataSet; + anObj: TObject; + Propinfo: PPropInfo; +begin + if Assigned(FSourceConnection) then + begin + Propinfo := GetPropInfo(PTypeInfo(OldDataSet.ClassInfo), 'Connection'); //ADO & Zeos + if not Assigned(Propinfo) then + Propinfo := GetPropInfo(PTypeInfo(OldDataSet.ClassInfo), 'SQLConnection'); //dbX + if not Assigned(Propinfo) then + Propinfo := GetPropInfo(PTypeInfo(OldDataSet.ClassInfo), 'Database'); //MySqlDAC + if not Assigned(Propinfo) then Exit; + anObj := GetObjectProp(OldDataSet, Propinfo); + if anObj <> FSourceConnection then Exit; + end; + + aDataSet := nil; + + if OldDataSet.ClassNameIs('TQuery') and (convBDE in FConvertComponents) or + OldDataSet.ClassNameIs('TZQuery') and (convZeos in FConvertComponents) or + OldDataSet.ClassNameIs('TSQLQuery') and (convDBX in FConvertComponents) or + OldDataSet.ClassNameIs('TSQLDataset') and (convDBX in FConvertComponents) or + OldDataSet.ClassNameIs('TADOQuery') and (convADO in FConvertComponents) or + OldDataSet.ClassNameIs('TADODataset') and (convADO in FConvertComponents) or + OldDataSet.ClassNameIs('TMySQLQuery') and (convMySQLDAC in FConvertComponents) then + {$WARNINGS OFF} //make D5 happy + aDataSet := TPSQLQuery.Create(OldDataSet.Owner) + {$WARNINGS ON} //make D5 happy + + else + + if OldDataSet.ClassNameIs('TTable') and (convBDE in FConvertComponents) or + OldDataSet.ClassNameIs('TZTable') and (convZeos in FConvertComponents) or + OldDataSet.ClassNameIs('TSQLTable') and (convDBX in FConvertComponents) or + OldDataSet.ClassNameIs('TADOTable') and (convADO in FConvertComponents) or + OldDataSet.ClassNameIs('TMySQLTable') and (convMySQLDAC in FConvertComponents) then + {$WARNINGS OFF} //make D5 happy + aDataSet := TPSQLTable.Create(OldDataSet.Owner) + {$WARNINGS ON} //make D5 happy + + else + + if OldDataSet.ClassNameIs('TStoredProc') and (convBDE in FConvertComponents) or + OldDataSet.ClassNameIs('TZStoredProc') and (convZeos in FConvertComponents) or + OldDataSet.ClassNameIs('TSQLStoredProc') and (convDBX in FConvertComponents) or + OldDataSet.ClassNameIs('TADOStoredProc') and (convADO in FConvertComponents) or + OldDataSet.ClassNameIs('TMySQLStoredProc') and (convMySQLDAC in FConvertComponents)then + {$WARNINGS OFF} //make D5 happy + aDataSet := TPSQLStoredProc.Create(OldDataSet.Owner) + {$WARNINGS ON} //make D5 happy + else + + if OldDataSet.ClassNameIs('TMySQLMacroQuery') and (convMySQLDAC in FConvertComponents) then + {$WARNINGS OFF} //make D5 happy + aDataSet := TPSQLMacroQuery.Create(OldDataSet.Owner); + {$WARNINGS ON} //make D5 happy + + if Assigned(aDataset) then + begin + UpdateDesignInfo(OldDataSet, aDataSet); + NewDataSetPair(aDataSet, OldDataSet); + end; +end; + +procedure TBDE2PSQLDAC.GetCachedUpdates(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); +begin + aDataSet.CachedUpdates := False; +end; + +procedure TBDE2PSQLDAC.GetNeededSQLs(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); +var AObj: TObject; +begin + if OldDataSet.ClassNameIs('TQuery') or OldDataSet.ClassNameIs('TZQuery') or + OldDataSet.ClassNameIs('TSQLQuery') or OldDataSet.ClassNameIs('TADOQuery') or + OldDataSet.ClassNameIs('TMySQLMacroQuery') then + begin + AObj := GetObjectProp(OldDataSet,'SQL'); + if Assigned(AObj) and (AObj is TStrings) then + TPSQLQuery(aDataSet).SQL.Assign(AObj as TStrings) + end + else + if OldDataSet.ClassNameIs('TSQLDataset') or OldDataSet.ClassNameIs('TADODataset') then + TPSQLQuery(aDataSet).SQL.Text := GetStrProp(OldDataSet, 'CommandText') + else + if OldDataSet.ClassNameIs('TTable') or OldDataSet.ClassNameIs('TZTable') or + OldDataSet.ClassNameIs('TSQLTable') or OldDataSet.ClassNameIs('TADOTable') then + begin + TPSQLTable(aDataSet).TableName := GetStrProp(OldDataSet, 'TableName'); + if GetPropInfo(OldDataset,'Filtered') <> nil then + TPSQLTable(aDataSet).Filtered := Boolean(GetOrdProp(OldDataSet, 'Filtered')); + if GetPropInfo(OldDataset,'Filter') <> nil then + TPSQLTable(aDataSet).Filter := GetStrProp(OldDataSet, 'Filter'); + end + else + if OldDataSet.ClassNameIs('TStoredProc') or OldDataSet.ClassNameIs('TZStoredProc') or + OldDataSet.ClassNameIs('TSQLStoredProc') then + TPSQLStoredProc(aDataSet).StoredProcName := GetStrProp(OldDataSet, 'StoredProcName') + else + if OldDataSet.ClassNameIs('TADOStoredProc') then + TPSQLStoredProc(aDataSet).StoredProcName := GetStrProp(OldDataSet, 'ProcedureName'); +end; + +procedure TBDE2PSQLDAC.GetNeededUpdateObject(aDataSet: TPSQLDataSet; OldDataSet: TDataSet); +var + Obj: TObject; + + procedure AssignSQL(const aSQL: TStrings; const PropName: string); + var AObj: TObject; + begin + try + AObj := GetObjectProp(Obj,PropName); + If Assigned(AObj) and (AObj is TStrings) then + aSQL.Assign(AObj as TStrings); + except + end; + end; + +begin + if GetPropInfo(OldDataset,'UpdateObject') <> nil then + Obj := GetObjectProp(OldDataSet, 'UpdateObject') + else + Exit; + if Assigned(Obj) and + (Obj.ClassNameIs('TUpdateSQL') or Obj.ClassNameIs('TZUpdateSQL')) then + begin + aDataset.UpdateObject := TPSQLUpdateSQL.Create((Obj as TComponent).Owner); + UpdateDesignInfo(Obj as TComponent,aDataset.UpdateObject); + (Obj as TComponent).Name := 'Die_' + (Obj as TComponent).Name; + aDataset.UpdateObject.Name := Copy((Obj as TComponent).Name,5,MaxInt); + AssignSQL(TPSQLUpdateSQL(aDataSet.UpdateObject).ModifySQL, 'ModifySQL'); + AssignSQL(TPSQLUpdateSQL(aDataSet.UpdateObject).InsertSQL, 'InsertSQL'); + AssignSQL(TPSQLUpdateSQL(aDataSet.UpdateObject).DeleteSQL, 'DeleteSQL'); + end; +end; + +procedure TBDE2PSQLDAC.FillComponents(ACompList: TList; + ACompClass: TClass); +var + I, J, K: Integer; + ModuleServices: IOTAModuleServices; + Module: IOTAModule; + Editor: IOTAEditor; + FormEditor: IOTAFormEditor; + RootComp: IOTAComponent; + Comp: TComponent; +begin + If not (Assigned(ACompList) and Assigned(ACompClass)) then + Exit; + + ModuleServices := BorlandIDEServices as IOTAModuleServices; + if ModuleServices = nil then Exit; + for I := 0 to ModuleServices.ModuleCount - 1 do + begin + Module := ModuleServices.Modules[I]; + for J := 0 to Module.GetModuleFileCount - 1 do + begin + Editor := Module.GetModuleFileEditor(J); + if Editor.QueryInterface(IOTAFormEditor, FormEditor) = S_OK then + begin + FormEditor.Show; + RootComp := FormEditor.GetRootComponent; + if RootComp <> nil then + begin + Comp := (RootComp as INTAComponent).GetComponent; + for K :=0 to Comp.ComponentCount-1 do + if Comp.Components[K] is ACompClass then + ACompList.Add(Comp.Components[K]); + end; + end; + end; + end; +end; + +procedure TBDE2PSQLDAC.SetDeleteSourceComponents(const Value: Boolean); +begin + FDeleteSourceComponents := Value; +end; + +procedure TBDE2PSQLDAC.SetSourceConnection(const Value: TCustomConnection); +begin + FSourceConnection := Value; + if Value <> nil then Value.FreeNotification(Self) +end; + +procedure TBDE2PSQLDAC.MigrateLookupFields; +var + FieldList: TList; + I: Integer; +begin + FieldList := TList.Create; + try + FillComponents(FieldList,TField); + for I := 0 to FieldList.Count-1 do + with TField(FieldList[i]) do + If Lookup then + LookupDataSet := FDataSets.GetPaired(LookupDataSet); + finally + FieldList.Free; + end; +end; + +procedure TBDE2PSQLDAC.SetConvertComponents( + const Value: TConvertComponents); +begin + FConvertComponents := Value; +end; + +end. + + + diff --git a/PSQLMonitor.pas b/Source/PSQLMonitor.pas similarity index 96% rename from PSQLMonitor.pas rename to Source/PSQLMonitor.pas index 6823f23..d805c5e 100644 --- a/PSQLMonitor.pas +++ b/Source/PSQLMonitor.pas @@ -1,1301 +1,1301 @@ -{$I pSQLDAC.inc} -unit PSQLMonitor; - -{SVN revision: $Id$} - -interface - -uses - SysUtils, {$IFDEF FPC}LCLIntf,{$ENDIF}{$IFDEF MSWINDOWS} Windows, Messages,{$ENDIF} - Classes, PSQLAccess, DB, PSQLDbTables, PSQLTypes - {$IFDEF DELPHI_16}, System.SyncObjs{$ENDIF} - {$IFDEF DELPHI_17}, System.Types{$ENDIF} - {$IFDEF NEXTGEN}, Generics.Collections{$ENDIF}; - -const - {$IFNDEF MSWINDOWS} - WM_USER = $400; - {$ENDIF} - - CM_BASE = $B000; - CM_RELEASE = CM_BASE + 33; - - WM_MIN_MONITOR = WM_USER; - WM_MAX_MONITOR = WM_USER + 512; - WM_SQL_EVENT = WM_MIN_MONITOR + 1; - - CRLF = #13#10; - -type - TPSQLCustomMonitor = class; - - EPSQLMonitorError = class(EPSQLDatabaseError);//mi:2007-04-27 EPSQLMonitorError inherited from EPSQLDACException - - - TPSQLTraceFlag = (tfQPrepare, tfQExecute, tfQFetch,tfConnect, tfTransact,tfMisc); - TPSQLTraceFlags = set of TPSQLTraceFlag; - - TSQLEvent = procedure(const Application, Database, Msg, SQL, ErrorMsg: string; - DataType: TPSQLTraceFlag; const ExecutedOK: boolean; EventTime: TDateTime) of object; - - TPSQLCustomMonitor = class(TComponent) - private - FHWnd: THandle; - FOnSQLEvent: TSQLEvent; - FTraceFlags: TPSQLTraceFlags; - FActive: Boolean; protected -{$IFDEF MSWINDOWS} - procedure MonitorWndProc(var Message : TMessage); -{$ENDIF} - procedure SetActive(const Value: Boolean); - procedure SetTraceFlags(const Value: TPSQLTraceFlags); - protected - property OnSQL : TSQLEvent read FOnSQLEvent write FOnSQLEvent; - property TraceFlags : TPSQLTraceFlags read FTraceFlags write SetTraceFlags; - property Active : Boolean read FActive write SetActive default true; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - procedure Release; - property Handle : THandle read FHwnd; - end; - - TPSQLMonitor = class(TPSQLCustomMonitor) - private - FAbout : TPSQLDACAbout; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property OnSQL; - property TraceFlags; - property Active; - end; - - TPSQLMonitorHook = class(TObject) - private - FActive: Boolean; - vEventsCreated : Boolean; - procedure CreateEvents; - protected - procedure WriteSQLData(const ADatabase, AMsg, ASQL: string; DataType: TPSQLTraceFlag; - AExecOK: boolean; const AErrorMsg: string = ''); - public - constructor Create; - destructor Destroy; override; - procedure TerminateWriteThread; - function SQLString(k:integer):Byte; - procedure RegisterMonitor(SQLMonitor : TPSQLCustomMonitor); - procedure UnregisterMonitor(SQLMonitor : TPSQLCustomMonitor); - procedure ReleaseMonitor(Arg : TPSQLCustomMonitor); - procedure SQLPrepare(qry: TNativeDataset); virtual; - procedure SQLExecute(qry: TNativeDataset; const AExecOK: boolean); overload; virtual; - procedure SQLExecute(db: TNativeConnect; const Sql: string; const AExecOK: boolean); overload; virtual; - procedure SQLFetch(qry: TNativeDataset); virtual; - procedure DBConnect(db: TNativeConnect; const AExecOK: boolean); virtual; - procedure DBDisconnect(db: TNativeConnect); virtual; - procedure TRStart(db: TNativeConnect; const AExecOK: boolean); virtual; - procedure TRCommit(db: TNativeConnect; const AExecOK: boolean); virtual; - procedure TRRollback(db: TNativeConnect; const AExecOK: boolean); virtual; - procedure SendMisc(Msg : String); - function GetEnabled: Boolean; - function GetMonitorCount : Integer; - procedure SetEnabled(const Value: Boolean); - property Enabled : Boolean read GetEnabled write SetEnabled default true; - end; - -function MonitorHook: TPSQLMonitorHook; -procedure EnableMonitoring; -procedure DisableMonitoring; -function MonitoringEnabled: Boolean; - - -implementation - -uses - Math{$IFNDEF FMX_AVAILABLE}, Forms{$ENDIF}; - -procedure MonError(ErrMess: String; const Args: array of const); -begin - raise EPSQLMonitorError.CreateFmt(ErrMess, Args); -end; - -function IsBlank(const Str: string) : boolean; -var - L: Integer; -begin - L := Length(Str); - while (L > 0) and (Str[L] <= ' ') do Dec(L); - result := L = 0; -end; - - -type - TPSQLTraceObject = Class(TObject) - private - FDataType : TPSQLTraceFlag; - FMsg : String; - FTimeStamp : TDateTime; - FDatabase: string; - FExecutedOK: boolean; - FSQL: string; - FApplication: string; - FErrorMsg: string; - public - constructor Create(const AAppName, ADatabase, AMsg, ASQL: string; - ADataType: TPSQLTraceFlag; const AExecOK: boolean; const AErrorMsg: string = ''); - - property DataType: TPSQLTraceFlag read FDataType; - property Application: string read FApplication; - property Msg: string read FMsg; - property SQL: string read FSQL; - property TimeStamp: TDateTime read FTimeStamp; - property Database: string read FDatabase; - property ExecutedOK: boolean read FExecutedOK; - property ErrorMsg: string read FErrorMsg; - end; - - TReleaseObject = Class(TObject) - private - FHandle : THandle; - public - constructor Create(Handle : THandle); - end; - - TMonitorWriterThread = class(TThread) - private - StopExec: boolean; - FMonitorMsgs : TList{$IFDEF NEXTGEN}{$ENDIF}; - protected - procedure Lock; - Procedure Unlock; - procedure BeginWrite; - procedure EndWrite; - procedure Execute; override; - procedure WriteToBuffer; - public - constructor Create; - destructor Destroy; override; - procedure WriteSQLData(const AAppName, ADatabase, AMsg, ASQL: String; - ADataType: TPSQLTraceFlag; AExecOK: boolean; const AErrorMsg: string = ''); - procedure ReleaseMonitor(HWnd : THandle); - end; - - TMonitorReaderThread = class(TThread) - private - st : TPSQLTraceObject; - FMonitors : TList{$IFDEF NEXTGEN}{$ENDIF}; - protected - procedure BeginRead; - procedure EndRead; - procedure ReadSQLData; - procedure Execute; override; - public - constructor Create; - destructor Destroy; override; - procedure AddMonitor(Arg : TPSQLCustomMonitor); - procedure RemoveMonitor(Arg : TPSQLCustomMonitor); - end; - -const - MonitorHookNames: array[0..5] of String = ( - 'PSQL.SQL.MONITOR.Mutex', - 'PSQL.SQL.MONITOR.SharedMem', - 'PSQL.SQL.MONITOR.WriteEvent', - 'PSQL.SQL.MONITOR.WriteFinishedEvent', - 'PSQL.SQL.MONITOR.ReadEvent', - 'PSQL.SQL.MONITOR.ReadFinishedEvent'); - - cMonitorHookSize = 2048; - cMaxBufferSize = cMonitorHookSize - (9 * SizeOf(Integer)) - SizeOf(TDateTime) - 2*SizeOf(Byte); - cDefaultTimeout = 1000; // 1 second - -var - {$IFNDEF NEXTGEN} - FAppSharedBuf, - FDBSharedBuf, - FMsgSharedBuf, - FSQLSharedBuf, - FErrSharedBuf, - FWriteLock, - FWriteEvent, - FWriteFinishedEvent, - FReadEvent, - FReadFinishedEvent : THandle; - - {$ENDIF} - - FAppBuffer, - FDBBuffer, - FMsgBuffer, - FSQLBuffer, - FErrBuffer: PAnsiDACChar; - - FMonitorCount, - {$IFNDEF NEXTGEN} - FReaderCount, - {$ENDIF} - FTraceDataType, - FQPrepareReaderCount, - FQExecuteReaderCount, - FQFetchReaderCount, - FConnectReaderCount, - FTransactReaderCount, - FAppBufSize, - FDBBufSize, - FMsgBufSize, - FSQLBufSize, - FErrBufSize, - FExecOK: PInteger; -// FBufferSize : PInteger; - FTimeStamp : PDateTime; - {$IFNDEF NEXTGEN} - FReserved : PByte; - FReserved1 : PByte; - {$ENDIF} - - FPSQLWriterThread : TMonitorWriterThread; - FPSQLReaderThread : TMonitorReaderThread; - - _MonitorHook: TPSQLMonitorHook; - - bDone: Boolean; -{$IFDEF DELPHI_16} - CS: TCriticalSection; -{$ELSE} - CS: TRTLCriticalSection; -{$ENDIF} - - bEnabledMonitoring:boolean; - -constructor TPSQLCustomMonitor.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FActive := true; - if not (csDesigning in ComponentState) then - begin - {$IFDEF MSWINDOWS} - FHWnd := {$IFDEF DELPHI_6}Classes.{$ENDIF}AllocateHWnd(MonitorWndProc); - {$ENDIF} - MonitorHook.RegisterMonitor(self); - end; - TraceFlags := [tfqPrepare .. tfTransact]; -end; -//---------------------------------------------------------------------------------------------------------------------- -destructor TPSQLCustomMonitor.Destroy(); -begin - if not (csDesigning in ComponentState) then - begin - if (tfQPrepare in TraceFlags) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQPrepareReaderCount^); - if (tfQExecute in TraceFlags) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQExecuteReaderCount^); - if (tfQFetch in TraceFlags) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQFetchReaderCount^); - if (tfConnect in TraceFlags) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FConnectReaderCount^); - if (tfTransact in TraceFlags) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FTransactReaderCount^); - if FActive then - MonitorHook.UnregisterMonitor(self); - {$IFDEF MSWINDOWS} - {$IFDEF DELPHI_6}Classes.{$ENDIF}DeallocateHwnd(FHWnd); - {$ENDIF MSWINDOWS} - end; - - inherited Destroy; -end; - -{$IFDEF MSWINDOWS} -procedure TPSQLCustomMonitor.MonitorWndProc(var Message: TMessage); -var - st : TPSQLTraceObject; -begin - case Message.Msg of - WM_SQL_EVENT: begin - st := TPSQLTraceObject(Message.LParam); - if (Assigned(FOnSQLEvent)) and - (st.FDataType in FTraceFlags) then - FOnSQLEvent(st.Application, st.Database, st.Msg, - st.SQL, st.ErrorMsg, st.DataType, st.ExecutedOK, st.TimeStamp); - st.Free; - end; - - CM_RELEASE : Free; - else - DefWindowProc(FHWnd, Message.Msg, Message.WParam, Message.LParam); - end; -end; -{$ENDIF} - -procedure TPSQLCustomMonitor.Release; -begin - MonitorHook.ReleaseMonitor(self); -end; - -procedure TPSQLCustomMonitor.SetActive(const Value: Boolean); -begin - if Value <> FActive then - begin - FActive := Value; - if not (csDesigning in ComponentState) then - if FActive then - Monitorhook.RegisterMonitor(self) else - MonitorHook.UnregisterMonitor(self); - end; -end; - -procedure TPSQLCustomMonitor.SetTraceFlags(const Value: TPSQLTraceFlags); -begin - if not (csDesigning in ComponentState) then - begin - if (tfQPrepare in TraceFlags) and not (tfQPrepare in Value) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQPrepareReaderCount^) else - if (not (tfQPrepare in TraceFlags)) and (tfQPrepare in Value) then - {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FQPrepareReaderCount^); - if (tfQExecute in TraceFlags) and not (tfQExecute in Value) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQExecuteReaderCount^) else - if (not (tfQExecute in TraceFlags)) and (tfQExecute in Value) then - {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FQExecuteReaderCount^); - if (tfQFetch in TraceFlags) and not (tfQFetch in Value) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQFetchReaderCount^) else - if (not (tfQFetch in TraceFlags)) and (tfQFetch in Value) then - {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FQFetchReaderCount^); - if (tfConnect in TraceFlags) and not (tfConnect in Value) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FConnectReaderCount^) else - if (not (tfConnect in TraceFlags)) and (tfConnect in Value) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FConnectReaderCount^); - if (tfTransact in TraceFlags) and not (tfTransact in Value) then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FTransactReaderCount^) else - if (not (tfTransact in TraceFlags)) and (tfTransact in Value) then - {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FTransactReaderCount^); - end; - FTraceFlags:=Value -end; - - -constructor TPSQLMonitorHook.Create; -begin - inherited Create; - vEventsCreated := false; - FActive := true; - if not vEventsCreated then - try - CreateEvents; - except - Enabled := false; - Exit; - end; -end; - - -procedure TPSQLMonitorHook.CreateEvents; -{$IFDEF MSWINDOWS} -var - Sa : TSecurityAttributes; - Sd : TSecurityDescriptor; - MapError: Integer; - - function CreateLocalEvent(Idx: Integer; InitialState: Boolean): THandle; - begin - Result := CreateEvent(@sa, true, InitialState, PChar(MonitorHookNames[Idx])); - if Result = 0 then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end; - - - function OpenLocalEvent(Idx: Integer): THandle; - begin - Result := OpenEvent(EVENT_ALL_ACCESS, true, PChar(MonitorHookNames[Idx])); - if Result = 0 then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end; - -begin - InitializeSecurityDescriptor(@Sd,SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(@Sd,true,nil,false); - Sa.nLength := SizeOf(Sa); - Sa.lpSecurityDescriptor := @Sd; - Sa.bInheritHandle := true; - - FAppSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, - 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '01')); - - MapError:=GetLastError; - if MapError= ERROR_ALREADY_EXISTS then - begin - FAppSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '01')); - if (FAppSharedBuf = 0) then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end else - begin - FWriteLock := CreateMutex(@sa, False, PChar(MonitorHookNames[0])); - FWriteEvent := CreateLocalEvent(2, False); - FWriteFinishedEvent := CreateLocalEvent(3, True); - FReadEvent := CreateLocalEvent(4, False); - FReadFinishedEvent := CreateLocalEvent(5, False); - end; - - FDBSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, - 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '02')); - - MapError := GetLastError(); - if MapError = ERROR_ALREADY_EXISTS then - begin - FDBSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '02')); - if (FDBSharedBuf = 0) then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end; - - FMsgSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, - 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '03')); - - MapError := GetLastError; - if MapError = ERROR_ALREADY_EXISTS then - begin - FMsgSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '03')); - if (FMsgSharedBuf = 0) then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end; - - FSQLSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, - 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '04')); - - MapError := GetLastError; - if MapError = ERROR_ALREADY_EXISTS then - begin - FSQLSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '04')); - if (FSQLSharedBuf = 0) then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end; - - FErrSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, - 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '05')); - - MapError := GetLastError; - if MapError = ERROR_ALREADY_EXISTS then - begin - FErrSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '05')); - if (FErrSharedBuf = 0) then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); - end; - -// FBuffer := MapViewOfFile(FSharedBuffer, FILE_MAP_ALL_ACCESS, 0, 0, 0); - FAppBuffer := MapViewOfFile(FAppSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); - FDBBuffer := MapViewOfFile(FDBSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); - FMsgBuffer := MapViewOfFile(FMsgSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); - FSQLBuffer := MapViewOfFile(FSQLSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); - FErrBuffer := MapViewOfFile(FErrSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); - - if FAppBuffer = nil then - MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); -// FMonitorCount := PInteger(FBuffer + cMonitorHookSize - SizeOf(Integer)); - FMonitorCount := PInteger(FAppBuffer + cMonitorHookSize - SizeOf(Integer)); - FReaderCount := PInteger(PAnsiChar(FMonitorCount) - SizeOf(Integer)); - FTraceDataType:= PInteger(PAnsiChar(FMonitorCount) - 2*SizeOf(Integer)); - FExecOK := PInteger(PAnsiChar(FMonitorCount) - 3*SizeOf(Integer)); -// FBufferSize := PInteger(PChar(FMonitorCount) - 3*SizeOf(Integer)); - FAppBufSize := PInteger(PAnsiChar(FMonitorCount) - 4*SizeOf(Integer)); - FDBBufSize := PInteger(PAnsiChar(FMonitorCount) - 5*SizeOf(Integer)); - FMsgBufSize := PInteger(PAnsiChar(FMonitorCount) - 6*SizeOf(Integer)); - FSQLBufSize := PInteger(PAnsiChar(FMonitorCount) - 7*SizeOf(Integer)); - FErrBufSize := PInteger(PAnsiChar(FMonitorCount) - 8*SizeOf(Integer)); - FQPrepareReaderCount:=PInteger(PAnsiChar(FMonitorCount) - 9*SizeOf(Integer)); - FQExecuteReaderCount:=PInteger(PAnsiChar(FMonitorCount) - 10*SizeOf(Integer)); - FQFetchReaderCount :=PInteger(PAnsiChar(FMonitorCount) - 11*SizeOf(Integer)); - FConnectReaderCount :=PInteger(PAnsiChar(FMonitorCount) - 12*SizeOf(Integer)); - FTransactReaderCount:=PInteger(PAnsiChar(FMonitorCount) - 13*SizeOf(Integer)); - FTimeStamp := PDateTime(PAnsiChar(FTransactReaderCount)- SizeOf(TDateTime)); - FReserved := PByte(PAnsiChar(FTimeStamp)- SizeOf(Byte)); - FReserved1 := PByte(PAnsiChar(FReserved )- SizeOf(Byte)); - if MapError= ERROR_ALREADY_EXISTS then - begin - FWriteLock := OpenMutex(MUTEX_ALL_ACCESS, False, PChar(MonitorHookNames[0])); - FWriteEvent := OpenLocalEvent(2); - FWriteFinishedEvent := OpenLocalEvent(3); - FReadEvent := OpenLocalEvent(4); - FReadFinishedEvent := OpenLocalEvent(5); - end else - begin - FMonitorCount^ :=0; - FReaderCount^ :=0; -// FBufferSize^ :=0; - FMsgBufSize^ :=0; - FQPrepareReaderCount^:=0; - FQExecuteReaderCount^:=0; - FQFetchReaderCount^ :=0; - FConnectReaderCount^ :=0; - FTransactReaderCount^:=0; - end; - if FMonitorCount^ < 0 then - FMonitorCount^ := 0; - if FReaderCount^ < 0 then - FReaderCount^ := 0; - vEventsCreated := true; -end; -{$ELSE} -begin -//no support for non-Windows environment yet -end; -{$ENDIF} - -function TPSQLMonitorHook.SQLString(k:integer):Byte; -begin - Result := 127 + k - k; -end; - -procedure TPSQLMonitorHook.DBConnect(db: TNativeConnect; const AExecOK: boolean); -{var - st : String;} -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FConnectReaderCount^>0) then - begin -// st := db.DBOptions.DatabaseName + ': [Connect]'; {do not localize} - WriteSQLData(db.DBOptions.DatabaseName, 'Connect', '', tfConnect, AExecOK, db.GetErrorText); - end; -end; - -procedure TPSQLMonitorHook.DBDisconnect(db: TNativeConnect); -{var - st: String;} -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FConnectReaderCount^>0) then - begin -// st := db.DBOptions.DatabaseName + ': [Disconnect]'; {do not localize} - WriteSQLData(db.DBOptions.DatabaseName, 'Disconnect', '', tfConnect, True); - end; -end; - -destructor TPSQLMonitorHook.Destroy; -begin -{$IFDEF MSWINDOWS} - if vEventsCreated then - begin -// UnmapViewOfFile(FBuffer); - UnmapViewOfFile(FAppBuffer); - UnmapViewOfFile(FDBBuffer); - UnmapViewOfFile(FMsgBuffer); - UnmapViewOfFile(FSQLBuffer); - UnmapViewOfFile(FErrBuffer); - CloseHandle(FAppSharedBuf); - CloseHandle(FDBSharedBuf); - CloseHandle(FMsgSharedBuf); - CloseHandle(FSQLSharedBuf); - CloseHandle(FErrSharedBuf); - -// CloseHandle(FSharedBuffer); - CloseHandle(FWriteEvent); - CloseHandle(FWriteFinishedEvent); - CloseHandle(FReadEvent); - CloseHandle(FReadFinishedEvent); - CloseHandle(FWriteLock); - end; -{$ENDIF} - inherited Destroy; -end; - -function TPSQLMonitorHook.GetEnabled: Boolean; -begin - Result := FActive; -end; - -function TPSQLMonitorHook.GetMonitorCount: Integer; -begin - if FMonitorCount = nil then - Result := 0 - else - Result := FMonitorCount^; -end; - -procedure TPSQLMonitorHook.RegisterMonitor(SQLMonitor: TPSQLCustomMonitor); -begin - if not vEventsCreated then - try - CreateEvents; - except - SQLMonitor.Active := false; - end; - if not Assigned(FPSQLReaderThread) then - FPSQLReaderThread := TMonitorReaderThread.Create; - FPSQLReaderThread.AddMonitor(SQLMonitor); -end; - -procedure TPSQLMonitorHook.ReleaseMonitor(Arg: TPSQLCustomMonitor); -begin - FPSQLWriterThread.ReleaseMonitor(Arg.FHWnd); -end; - -procedure TPSQLMonitorHook.SendMisc(Msg: String); -begin - if FActive then - WriteSQLData('', Msg, '', tfMisc, False); -end; - -procedure TPSQLMonitorHook.SetEnabled(const Value: Boolean); -begin - if FActive <> Value then - FActive := Value; - if (not FActive) and (Assigned(FPSQLWriterThread)) then - begin - FPSQLWriterThread.Terminate; - FPSQLWriterThread.WaitFor; - FPSQLWriterThread.Free; - FPSQLWriterThread := nil; - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TPSQLMonitorHook.SQLExecute(qry: TNativeDataset; const AExecOK: boolean); -var - st: string; -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FQExecuteReaderCount^ > 0) - then - begin - if qry.SQLQuery <> '' then - st := qry.SQLQuery - else - st := string(qry.TableName); - - WriteSQLData(qry.Connect.DBOptions.DatabaseName, 'Execute', st, - tfQExecute, AExecOK, qry.Connect.GetErrorText); - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TPSQLMonitorHook.SQLExecute(db: TNativeConnect; const Sql: string; const AExecOK: boolean); -begin - if FActive and bEnabledMonitoring and (GetMonitorCount > 0) and - (FQExecuteReaderCount^ > 0) - then - begin - WriteSQLData(db.DBOptions.DatabaseName, 'Execute', Sql, - tfQExecute, AExecOK, db.GetErrorText); - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TPSQLMonitorHook.SQLFetch(qry: TNativeDataset); -var - st: String; -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FQFetchReaderCount^>0) - then - begin - if qry.SQLQuery <> '' then - st := 'Query' - else - st := string(qry.TableName); - - st := st + ': Row # '+ IntToStr(qry.RecordNumber) + CRLF; - WriteSQLData(qry.Connect.DBOptions.DatabaseName, 'Fetch', st, tfQFetch, True); - end; -end; - -procedure TPSQLMonitorHook.SQLPrepare(qry: TNativeDataset); -var - st: String; -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FQPrepareReaderCount^>0) - then - begin - if qry.SQLQuery <> '' then - st := qry.SQLQuery - else - st := string(qry.TableName); - - WriteSQLData(qry.Connect.DBOptions.DatabaseName, 'Prepare', st, tfQPrepare, True); - end; -end; - -procedure TPSQLMonitorHook.TRCommit(db: TNativeConnect; const AExecOK: boolean); -{var - st: String;} -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FTransactReaderCount^>0) then - begin -// st := db.DBOptions.DatabaseName + ': [Commit (Hard commit)]'; - WriteSQLData(db.DBOptions.DatabaseName, 'Commit (Hard commit)', '', - tfTransact, AExecOK, db.GetErrorText); - end; -end; - -procedure TPSQLMonitorHook.TRRollback(db: TNativeConnect; const AExecOK: boolean); -{var - st: String;} -begin - if FActive and bEnabledMonitoring and (GetMonitorCount>0) - and (FTransactReaderCount^>0) then - begin -// st := db.DBOptions.DatabaseName + ': [Rollback]'; - WriteSQLData(db.DBOptions.DatabaseName, 'Rollback', '', tfTransact, - AExecOK, db.GetErrorText); - end; -end; - -procedure TPSQLMonitorHook.TRStart(db: TNativeConnect; const AExecOK: boolean); -{var - st: String;} -begin - if FActive and bEnabledMonitoring and bEnabledMonitoring and (GetMonitorCount>0) - and (FTransactReaderCount^>0) then - begin -// st := db.DBOptions.DatabaseName + ': [Start transaction]'; -// WriteSQLData(st, tfTransact); - WriteSQLData(db.DBOptions.DatabaseName, 'Start transaction', '', tfTransact, - AExecOK, db.GetErrorText); - end; -end; - -procedure TPSQLMonitorHook.UnregisterMonitor(SQLMonitor: TPSQLCustomMonitor); -begin - FPSQLReaderThread.RemoveMonitor(SQLMonitor); - if FPSQLReaderThread.FMonitors.Count = 0 then - begin - FPSQLReaderThread.Terminate; - if not Assigned(FPSQLWriterThread) then - begin - FPSQLWriterThread := TMonitorWriterThread.Create; - end; - FPSQLWriterThread.WriteSQLData('', '', '', '', tfMisc, True); - FPSQLReaderThread.WaitFor; - FPSQLReaderThread.Free; - FPSQLReaderThread:=nil; - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TPSQLMonitorHook.WriteSQLData(const ADatabase, AMsg, ASQL: string; - DataType: TPSQLTraceFlag; AExecOK: boolean; const AErrorMsg: string); -var - AppName: string; -begin - if not vEventsCreated then - begin - try - CreateEvents; - except - Enabled := false; - Exit; - end; - end; - - {$IFDEF FMX_AVAILABLE} - AppName := ''; //cannot use TApplication.Title 'cause have no idea if it's FMX or VCL - {$ELSE} - AppName := Application.Title; - {$ENDIF} - if not Assigned(FPSQLWriterThread) then - FPSQLWriterThread := TMonitorWriterThread.Create; - - FPSQLWriterThread.WriteSQLData(AppName, ADatabase, AMsg, ASQL, DataType, AExecOK, AErrorMsg); -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TPSQLMonitorHook.TerminateWriteThread; -begin - if Assigned(FPSQLWriterThread) then - begin - FPSQLWriterThread.Free; - FPSQLWriterThread:=nil - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -constructor TMonitorWriterThread.Create; -begin - StopExec := False; - FMonitorMsgs := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - inherited Create(False); - if FMonitorCount^ = 0 then - {$WARNINGS OFF} - Suspend; - {$WARNINGS ON} -end; -//---------------------------------------------------------------------------------------------------------------------- -destructor TMonitorWriterThread.Destroy; -var - Msg:TObject; -begin - {$WARNINGS OFF} - Resume; - {$WARNINGS ON} - - if FMonitorMsgs.Count>0 then - begin - Msg:=FMonitorMsgs[0]; - FMonitorMsgs.Delete(0); - Msg.Free; - end; - - FMonitorMsgs.Free; - FMonitorMsgs := nil;//mi:2006-09-15 - inherited Destroy; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.Execute; -begin -{$IFDEF MSWINDOWS} - while (Assigned(FMonitorMsgs)) and not StopExec do - begin - if (Terminated or bDone) and (FMonitorMsgs.Count = 0) then//mi:2006-09-15 removed from while condition to ensure FMonitorMsgs<>nil - break; - - if (FMonitorCount^ = 0) then - begin - while FMonitorMsgs.Count <> 0 do - FMonitorMsgs.Remove(FMonitorMsgs[0]); - {$WARNINGS OFF} - Suspend; - {$WARNINGS ON} - end - - else - - if FMonitorMsgs.Count <> 0 then - begin - if (TObject(FMonitorMsgs.Items[0]) is TReleaseObject) then - PostMessage(TReleaseObject(FMonitorMsgs.Items[0]).FHandle, CM_RELEASE, 0, 0) - else - begin - if bEnabledMonitoring then - WriteToBuffer() - else - begin - BeginWrite(); - TPSQLTraceObject(FMonitorMsgs[0]).Free; - FMonitorMsgs.Delete(0); - EndWrite(); - end; - end; - end - else - {$WARNINGS OFF} - Suspend; - {$WARNINGS ON} - end; -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.Lock; -begin -{$IFDEF MSWINDOWS} - WaitForSingleObject(FWriteLock, INFINITE); -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.Unlock; -begin -{$IFDEF MSWINDOWS} - ReleaseMutex(FWriteLock); -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.WriteSQLData(const AAppName, ADatabase, AMsg, ASQL: String; - ADataType: TPSQLTraceFlag; AExecOK: boolean; const AErrorMsg: string); -var - mto : TPSQLTraceObject; -begin - if (FMonitorCount^ <> 0) then - begin - mto := TPSQLTraceObject.Create(AAppName, ADatabase, AMsg, ASQL, ADataType, AExecOK, AErrorMsg); - FMonitorMsgs.Add(mto); - - {$WARNINGS OFF} - Resume; - {$WARNINGS ON} - end - else - begin - FreeAndNil(FPSQLWriterThread) - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.BeginWrite; -begin - Lock(); -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.EndWrite; -begin - { - * 1. Wait to end the write until all registered readers have - * started to wait for a write event - * 2. Block all of those waiting for the write to finish. - * 3. Block all of those waiting for all readers to finish. - * 4. Unblock all readers waiting for a write event. - * 5. Wait until all readers have finished reading. - * 6. Now, block all those waiting for a write event. - * 7. Unblock all readers waiting for a write to be finished. - * 8. Unlock the mutex. - } -{$IFDEF MSWINDOWS} - while WaitForSingleObject(FReadEvent, cDefaultTimeout) = WAIT_TIMEOUT do - begin - if FMonitorCount^ > 0 then - InterlockedDecrement(FMonitorCount^); - - if (FReaderCount^ = FMonitorCount^ - 1) or (FMonitorCount^ = 0) then - SetEvent(FReadEvent); - end; - - ResetEvent(FWriteFinishedEvent); - ResetEvent(FReadFinishedEvent); - SetEvent(FWriteEvent); { Let all readers pass through. } - - while WaitForSingleObject(FReadFinishedEvent, cDefaultTimeout) = WAIT_TIMEOUT do - begin - if (FReaderCount^ = 0) or (InterlockedDecrement(FReaderCount^) = 0) then - SetEvent(FReadFinishedEvent); - end; - - ResetEvent(FWriteEvent); - SetEvent(FWriteFinishedEvent); - Unlock(); -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.WriteToBuffer(); -{$IFDEF MSWINDOWS} -//local procedures - procedure _WriteStrToBuf(const S: string; Buf: PAnsiChar; BufSize: PInteger); - var - i, len: Integer; - Text: String; - ptr : {$IFDEF DELPHI_12}PByte{$ELSE}PChar{$ENDIF}; - begin - Text := EmptyStr; - - for i := 1 to length(S) do - begin - if ((S[i] >= #0) and (S[i] <= #9)) - or (S[i] = #$B) - or (S[i] = #$C) - or ((S[i] >= #$E) and (S[i] <= #31)) - then - Text := Text + '#$' + IntToHex(ord(S[i]), 2) - else - Text := Text + S[i]; - end; - - len := Length(Text) * sizeof(Char);//mi:2008-11-12 unicode compatibility - ptr := {$IFDEF DELPHI_12}PByte{$ELSE}PChar{$ENDIF}(Text);//mi:2008-11-12 - - BufSize^ := 0; -// Move(#0, Buf[0], BufSize^); - while (len > 0) do - begin - BufSize^ := Min(len, cMaxBufferSize); -// Move(ptr, Buf, BufSize^); - CopyMemory(pointer(Buf), ptr, BufSize^); - Inc(ptr, cMaxBufferSize); - Dec(len, cMaxBufferSize); - end; - end; - -begin - Lock(); - - try - if FMonitorCount^ = 0 then - begin - FMonitorMsgs.Remove(FMonitorMsgs[0]); - end - else - begin - BeginWrite(); - - try - _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).Application, FAppBuffer, FAppBufSize); - _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).Database, FDBBuffer, FDBBufSize); - _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).Msg, FMsgBuffer, FMsgBufSize); - _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).SQL, FSQLBuffer, FSQLBufSize); - _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).ErrorMsg, FErrBuffer, FErrBufSize); - - FTraceDataType^ := Integer(TPSQLTraceObject(FMonitorMsgs[0]).DataType); - FTimeStamp^ := TPSQLTraceObject(FMonitorMsgs[0]).TimeStamp; - FExecOK^ := Ord(TPSQLTraceObject(FMonitorMsgs[0]).ExecutedOK); - finally - EndWrite(); - end; - end; - - if FMonitorMsgs.Count > 0 then - begin - TPSQLTraceObject(FMonitorMsgs[0]).Free(); - FMonitorMsgs.Delete(0); - end; - finally - Unlock(); - end; -end; -{$ELSE} -begin -end; -{$ENDIF} -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorWriterThread.ReleaseMonitor(HWnd: THandle); -begin - FMonitorMsgs.Add(TReleaseObject.Create(HWnd)); -end; - -{ TPSQLTraceObject } - -constructor TPSQLTraceObject.Create(const AAppName, ADatabase, AMsg, ASQL: string; - ADataType: TPSQLTraceFlag; const AExecOK: boolean; const AErrorMsg: string); -begin - FApplication := AAppName; - FDatabase := ADatabase; - FMsg := AMsg; - FSQL := ASQL; - FDataType := ADataType; - FExecutedOK := AExecOK; - FTimeStamp := Now; - FErrorMsg := AErrorMsg; -end; - -{TReleaseObject} - -constructor TReleaseObject.Create(Handle: THandle); -begin - FHandle := Handle; -end; - -{ReaderThread} - -procedure TMonitorReaderThread.AddMonitor(Arg: TPSQLCustomMonitor); -begin -{$IFDEF DELPHI_16} - CS.Enter; -{$ELSE} - EnterCriticalSection(CS); -{$ENDIF} - try - if FMonitors.IndexOf(Arg) < 0 then - FMonitors.Add(Arg); - finally - {$IFDEF DELPHI_16} - CS.Leave; - {$ELSE} - LeaveCriticalSection(CS); - {$ENDIF} - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorReaderThread.BeginRead(); -begin -{$IFDEF MSWINDOWS} - { - * 1. Wait for the "previous" write event to complete. - * 2. Increment the number of readers. - * 3. if the reader count is the number of interested readers, then - * inform the system that all readers are ready. - * 4. Finally, wait for the FWriteEvent to signal. - } - WaitForSingleObject(FWriteFinishedEvent, INFINITE); - InterlockedIncrement(FReaderCount^); - if FReaderCount^ = FMonitorCount^ then - SetEvent(FReadEvent); - WaitForSingleObject(FWriteEvent, INFINITE); -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -constructor TMonitorReaderThread.Create; -begin - inherited Create(true); -{$IFDEF MSWINDOWS} - st := TPSQLTraceObject.Create('', '', '', '', tfMisc, True); - FMonitors := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; - InterlockedIncrement(FMonitorCount^); - if Suspended then - {$WARNINGS OFF} - Resume; - {$WARNINGS ON} -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -destructor TMonitorReaderThread.Destroy; -begin -{$IFDEF MSWINDOWS} - if FMonitorCount^ > 0 then - {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FMonitorCount^); - FreeAndNil(FMonitors); - FreeAndNil(st); -{$ENDIF} - inherited Destroy; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorReaderThread.EndRead; -begin -{$IFDEF MSWINDOWS} - if InterlockedDecrement(FReaderCount^) = 0 then - begin - ResetEvent(FReadEvent); - SetEvent(FReadFinishedEvent); - end; -{$ENDIF} -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorReaderThread.Execute; -var - i : Integer; - FTemp : TPSQLTraceObject; -begin - while (not Terminated) and (not bDone) do - begin - ReadSQLData(); - - if not IsBlank(st.FMsg) then - for i := 0 to FMonitors.Count - 1 do - begin - FTemp := TPSQLTraceObject.Create(st.Application, st.Database, - st.Msg, st.SQL, st.FDataType, st.ExecutedOK, st.ErrorMsg); -{$IFDEF MSWINDOWS} - PostMessage(TPSQLCustomMonitor(FMonitors[i]).Handle, - WM_SQL_EVENT, - 0, - LPARAM(FTemp)); -{$ENDIF} - end; - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorReaderThread.ReadSQLData(); - function _ReadStr(Buffer : PAnsiDACChar; Len : Cardinal) : string; - begin - {$IFDEF DELPHI_12} - SetString(Result, PChar(Buffer), Len div sizeof(char)); - {$ELSE} - SetString(Result, Buffer, Len); - {$ENDIF} - end; -begin - st.FMsg := ''; - st.FApplication := ''; - st.FDatabase := ''; - st.FSQL := ''; - st.FErrorMsg := ''; - - BeginRead(); - if not bDone then - try - st.FApplication := _ReadStr(FAppBuffer, FAppBufSize^); - st.FDatabase := _ReadStr(FDBBuffer, FDBBufSize^); - st.FMsg := _ReadStr(FMsgBuffer, FMsgBufSize^); - st.FSQL := _ReadStr(FSQLBuffer, FSQLBufSize^); - st.FErrorMsg := _ReadStr(FErrBuffer, FErrBufSize^); - - st.FDataType := TPSQLTraceFlag(FTraceDataType^); - st.FTimeStamp := TDateTime(FTimeStamp^); - st.FExecutedOK := Boolean(FExecOK^); - finally - EndRead(); - end; -end; -//---------------------------------------------------------------------------------------------------------------------- -procedure TMonitorReaderThread.RemoveMonitor(Arg: TPSQLCustomMonitor); -begin -{$IFDEF DELPHI_16} - CS.Enter; -{$ELSE} - EnterCriticalSection(CS); -{$ENDIF} - try - FMonitors.Remove(Arg); - finally - {$IFDEF DELPHI_16} - CS.Leave; - {$ELSE} - LeaveCriticalSection(CS); - {$ENDIF} - end; -end; - -function MonitorHook: TPSQLMonitorHook; -begin - if (_MonitorHook = nil) and (not bDone) then - begin - {$IFDEF DELPHI_16} - CS.Enter; - {$ELSE} - EnterCriticalSection(CS); - {$ENDIF} - - if (_MonitorHook = nil) and (not bDone) then - begin - _MonitorHook := TPSQLMonitorHook.Create; - end; - - {$IFDEF DELPHI_16} - CS.Leave; - {$ELSE} - LeaveCriticalSection(CS); - {$ENDIF} - end; - Result := _MonitorHook -end; - -procedure EnableMonitoring; -begin - bEnabledMonitoring := true; -end; - -procedure DisableMonitoring; -begin - bEnabledMonitoring :=false; -end; - -function MonitoringEnabled: Boolean; -begin - Result := bEnabledMonitoring; -end; - - -initialization -{$IFDEF DELPHI_16} - CS := TCriticalSection.Create; -{$ELSE} - InitializeCriticalSection(CS); -{$ENDIF} - _MonitorHook := nil; - FPSQLWriterThread := nil; - FPSQLReaderThread := nil; - bDone := False; - bEnabledMonitoring := True; - -finalization - try - bDone := True; - - if Assigned(FPSQLWriterThread) then - begin - {$WARNINGS OFF} - FPSQLWriterThread.Resume; - {$WARNINGS ON} - FPSQLWriterThread.Terminate; - FPSQLWriterThread.StopExec := True; - {$IFDEF DELPHI_6} - FPSQLWriterThread.WaitFor; - {$ENDIF} - end; - - if Assigned(FPSQLReaderThread) then - begin - {$WARNINGS OFF} - FPSQLReaderThread.Resume; - {$WARNINGS ON} - FPSQLReaderThread.Terminate; - {$IFDEF DELPHI_6} - FPSQLReaderThread.WaitFor; - {$ENDIF} - end; - - FreeAndNil(_MonitorHook); - - finally - {$IFDEF DELPHI_16} - CS.Free; - {$ELSE} - DeleteCriticalSection(CS); - {$ENDIF} - end; -end. - +{$I pSQLDAC.inc} +unit PSQLMonitor; + +{SVN revision: $Id$} + +interface + +uses + SysUtils, {$IFDEF FPC}LCLIntf,{$ENDIF}{$IFDEF MSWINDOWS} Windows, Messages,{$ENDIF} + Classes, PSQLAccess, DB, PSQLDbTables, PSQLTypes + {$IFDEF DELPHI_16}, System.SyncObjs{$ENDIF} + {$IFDEF DELPHI_17}, System.Types{$ENDIF} + {$IFDEF NEXTGEN}, Generics.Collections{$ENDIF}; + +const + {$IFNDEF MSWINDOWS} + WM_USER = $400; + {$ENDIF} + + CM_BASE = $B000; + CM_RELEASE = CM_BASE + 33; + + WM_MIN_MONITOR = WM_USER; + WM_MAX_MONITOR = WM_USER + 512; + WM_SQL_EVENT = WM_MIN_MONITOR + 1; + + CRLF = #13#10; + +type + TPSQLCustomMonitor = class; + + EPSQLMonitorError = class(EPSQLDatabaseError);//mi:2007-04-27 EPSQLMonitorError inherited from EPSQLDACException + + + TPSQLTraceFlag = (tfQPrepare, tfQExecute, tfQFetch,tfConnect, tfTransact,tfMisc); + TPSQLTraceFlags = set of TPSQLTraceFlag; + + TSQLEvent = procedure(const Application, Database, Msg, SQL, ErrorMsg: string; + DataType: TPSQLTraceFlag; const ExecutedOK: boolean; EventTime: TDateTime) of object; + + TPSQLCustomMonitor = class(TComponent) + private + FHWnd: THandle; + FOnSQLEvent: TSQLEvent; + FTraceFlags: TPSQLTraceFlags; + FActive: Boolean; protected +{$IFDEF MSWINDOWS} + procedure MonitorWndProc(var Message : TMessage); +{$ENDIF} + procedure SetActive(const Value: Boolean); + procedure SetTraceFlags(const Value: TPSQLTraceFlags); + protected + property OnSQL : TSQLEvent read FOnSQLEvent write FOnSQLEvent; + property TraceFlags : TPSQLTraceFlags read FTraceFlags write SetTraceFlags; + property Active : Boolean read FActive write SetActive default true; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure Release; + property Handle : THandle read FHwnd; + end; + + TPSQLMonitor = class(TPSQLCustomMonitor) + private + FAbout : TPSQLDACAbout; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property OnSQL; + property TraceFlags; + property Active; + end; + + TPSQLMonitorHook = class(TObject) + private + FActive: Boolean; + vEventsCreated : Boolean; + procedure CreateEvents; + protected + procedure WriteSQLData(const ADatabase, AMsg, ASQL: string; DataType: TPSQLTraceFlag; + AExecOK: boolean; const AErrorMsg: string = ''); + public + constructor Create; + destructor Destroy; override; + procedure TerminateWriteThread; + function SQLString(k:integer):Byte; + procedure RegisterMonitor(SQLMonitor : TPSQLCustomMonitor); + procedure UnregisterMonitor(SQLMonitor : TPSQLCustomMonitor); + procedure ReleaseMonitor(Arg : TPSQLCustomMonitor); + procedure SQLPrepare(qry: TNativeDataset); virtual; + procedure SQLExecute(qry: TNativeDataset; const AExecOK: boolean); overload; virtual; + procedure SQLExecute(db: TNativeConnect; const Sql: string; const AExecOK: boolean); overload; virtual; + procedure SQLFetch(qry: TNativeDataset); virtual; + procedure DBConnect(db: TNativeConnect; const AExecOK: boolean); virtual; + procedure DBDisconnect(db: TNativeConnect); virtual; + procedure TRStart(db: TNativeConnect; const AExecOK: boolean); virtual; + procedure TRCommit(db: TNativeConnect; const AExecOK: boolean); virtual; + procedure TRRollback(db: TNativeConnect; const AExecOK: boolean); virtual; + procedure SendMisc(Msg : String); + function GetEnabled: Boolean; + function GetMonitorCount : Integer; + procedure SetEnabled(const Value: Boolean); + property Enabled : Boolean read GetEnabled write SetEnabled default true; + end; + +function MonitorHook: TPSQLMonitorHook; +procedure EnableMonitoring; +procedure DisableMonitoring; +function MonitoringEnabled: Boolean; + + +implementation + +uses + Math{$IFNDEF FMX_AVAILABLE}, Forms{$ENDIF}; + +procedure MonError(ErrMess: String; const Args: array of const); +begin + raise EPSQLMonitorError.CreateFmt(ErrMess, Args); +end; + +function IsBlank(const Str: string) : boolean; +var + L: Integer; +begin + L := Length(Str); + while (L > 0) and (Str[L] <= ' ') do Dec(L); + result := L = 0; +end; + + +type + TPSQLTraceObject = Class(TObject) + private + FDataType : TPSQLTraceFlag; + FMsg : String; + FTimeStamp : TDateTime; + FDatabase: string; + FExecutedOK: boolean; + FSQL: string; + FApplication: string; + FErrorMsg: string; + public + constructor Create(const AAppName, ADatabase, AMsg, ASQL: string; + ADataType: TPSQLTraceFlag; const AExecOK: boolean; const AErrorMsg: string = ''); + + property DataType: TPSQLTraceFlag read FDataType; + property Application: string read FApplication; + property Msg: string read FMsg; + property SQL: string read FSQL; + property TimeStamp: TDateTime read FTimeStamp; + property Database: string read FDatabase; + property ExecutedOK: boolean read FExecutedOK; + property ErrorMsg: string read FErrorMsg; + end; + + TReleaseObject = Class(TObject) + private + FHandle : THandle; + public + constructor Create(Handle : THandle); + end; + + TMonitorWriterThread = class(TThread) + private + StopExec: boolean; + FMonitorMsgs : TList{$IFDEF NEXTGEN}{$ENDIF}; + protected + procedure Lock; + Procedure Unlock; + procedure BeginWrite; + procedure EndWrite; + procedure Execute; override; + procedure WriteToBuffer; + public + constructor Create; + destructor Destroy; override; + procedure WriteSQLData(const AAppName, ADatabase, AMsg, ASQL: String; + ADataType: TPSQLTraceFlag; AExecOK: boolean; const AErrorMsg: string = ''); + procedure ReleaseMonitor(HWnd : THandle); + end; + + TMonitorReaderThread = class(TThread) + private + st : TPSQLTraceObject; + FMonitors : TList{$IFDEF NEXTGEN}{$ENDIF}; + protected + procedure BeginRead; + procedure EndRead; + procedure ReadSQLData; + procedure Execute; override; + public + constructor Create; + destructor Destroy; override; + procedure AddMonitor(Arg : TPSQLCustomMonitor); + procedure RemoveMonitor(Arg : TPSQLCustomMonitor); + end; + +const + MonitorHookNames: array[0..5] of String = ( + 'PSQL.SQL.MONITOR.Mutex', + 'PSQL.SQL.MONITOR.SharedMem', + 'PSQL.SQL.MONITOR.WriteEvent', + 'PSQL.SQL.MONITOR.WriteFinishedEvent', + 'PSQL.SQL.MONITOR.ReadEvent', + 'PSQL.SQL.MONITOR.ReadFinishedEvent'); + + cMonitorHookSize = 2048; + cMaxBufferSize = cMonitorHookSize - (9 * SizeOf(Integer)) - SizeOf(TDateTime) - 2*SizeOf(Byte); + cDefaultTimeout = 1000; // 1 second + +var + {$IFNDEF NEXTGEN} + FAppSharedBuf, + FDBSharedBuf, + FMsgSharedBuf, + FSQLSharedBuf, + FErrSharedBuf, + FWriteLock, + FWriteEvent, + FWriteFinishedEvent, + FReadEvent, + FReadFinishedEvent : THandle; + + {$ENDIF} + + FAppBuffer, + FDBBuffer, + FMsgBuffer, + FSQLBuffer, + FErrBuffer: PAnsiDACChar; + + FMonitorCount, + {$IFNDEF NEXTGEN} + FReaderCount, + {$ENDIF} + FTraceDataType, + FQPrepareReaderCount, + FQExecuteReaderCount, + FQFetchReaderCount, + FConnectReaderCount, + FTransactReaderCount, + FAppBufSize, + FDBBufSize, + FMsgBufSize, + FSQLBufSize, + FErrBufSize, + FExecOK: PInteger; +// FBufferSize : PInteger; + FTimeStamp : PDateTime; + {$IFNDEF NEXTGEN} + FReserved : PByte; + FReserved1 : PByte; + {$ENDIF} + + FPSQLWriterThread : TMonitorWriterThread; + FPSQLReaderThread : TMonitorReaderThread; + + _MonitorHook: TPSQLMonitorHook; + + bDone: Boolean; +{$IFDEF DELPHI_16} + CS: TCriticalSection; +{$ELSE} + CS: TRTLCriticalSection; +{$ENDIF} + + bEnabledMonitoring:boolean; + +constructor TPSQLCustomMonitor.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FActive := true; + if not (csDesigning in ComponentState) then + begin + {$IFDEF MSWINDOWS} + FHWnd := {$IFDEF DELPHI_6}Classes.{$ENDIF}AllocateHWnd(MonitorWndProc); + {$ENDIF} + MonitorHook.RegisterMonitor(self); + end; + TraceFlags := [tfqPrepare .. tfTransact]; +end; +//---------------------------------------------------------------------------------------------------------------------- +destructor TPSQLCustomMonitor.Destroy(); +begin + if not (csDesigning in ComponentState) then + begin + if (tfQPrepare in TraceFlags) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQPrepareReaderCount^); + if (tfQExecute in TraceFlags) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQExecuteReaderCount^); + if (tfQFetch in TraceFlags) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQFetchReaderCount^); + if (tfConnect in TraceFlags) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FConnectReaderCount^); + if (tfTransact in TraceFlags) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FTransactReaderCount^); + if FActive then + MonitorHook.UnregisterMonitor(self); + {$IFDEF MSWINDOWS} + {$IFDEF DELPHI_6}Classes.{$ENDIF}DeallocateHwnd(FHWnd); + {$ENDIF MSWINDOWS} + end; + + inherited Destroy; +end; + +{$IFDEF MSWINDOWS} +procedure TPSQLCustomMonitor.MonitorWndProc(var Message: TMessage); +var + st : TPSQLTraceObject; +begin + case Message.Msg of + WM_SQL_EVENT: begin + st := TPSQLTraceObject(Message.LParam); + if (Assigned(FOnSQLEvent)) and + (st.FDataType in FTraceFlags) then + FOnSQLEvent(st.Application, st.Database, st.Msg, + st.SQL, st.ErrorMsg, st.DataType, st.ExecutedOK, st.TimeStamp); + st.Free; + end; + + CM_RELEASE : Free; + else + DefWindowProc(FHWnd, Message.Msg, Message.WParam, Message.LParam); + end; +end; +{$ENDIF} + +procedure TPSQLCustomMonitor.Release; +begin + MonitorHook.ReleaseMonitor(self); +end; + +procedure TPSQLCustomMonitor.SetActive(const Value: Boolean); +begin + if Value <> FActive then + begin + FActive := Value; + if not (csDesigning in ComponentState) then + if FActive then + Monitorhook.RegisterMonitor(self) else + MonitorHook.UnregisterMonitor(self); + end; +end; + +procedure TPSQLCustomMonitor.SetTraceFlags(const Value: TPSQLTraceFlags); +begin + if not (csDesigning in ComponentState) then + begin + if (tfQPrepare in TraceFlags) and not (tfQPrepare in Value) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQPrepareReaderCount^) else + if (not (tfQPrepare in TraceFlags)) and (tfQPrepare in Value) then + {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FQPrepareReaderCount^); + if (tfQExecute in TraceFlags) and not (tfQExecute in Value) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQExecuteReaderCount^) else + if (not (tfQExecute in TraceFlags)) and (tfQExecute in Value) then + {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FQExecuteReaderCount^); + if (tfQFetch in TraceFlags) and not (tfQFetch in Value) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FQFetchReaderCount^) else + if (not (tfQFetch in TraceFlags)) and (tfQFetch in Value) then + {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FQFetchReaderCount^); + if (tfConnect in TraceFlags) and not (tfConnect in Value) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FConnectReaderCount^) else + if (not (tfConnect in TraceFlags)) and (tfConnect in Value) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FConnectReaderCount^); + if (tfTransact in TraceFlags) and not (tfTransact in Value) then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FTransactReaderCount^) else + if (not (tfTransact in TraceFlags)) and (tfTransact in Value) then + {$IFDEF DELPHI_16}TInterlocked.Increment{$ELSE}InterlockedIncrement{$ENDIF}(FTransactReaderCount^); + end; + FTraceFlags:=Value +end; + + +constructor TPSQLMonitorHook.Create; +begin + inherited Create; + vEventsCreated := false; + FActive := true; + if not vEventsCreated then + try + CreateEvents; + except + Enabled := false; + Exit; + end; +end; + + +procedure TPSQLMonitorHook.CreateEvents; +{$IFDEF MSWINDOWS} +var + Sa : TSecurityAttributes; + Sd : TSecurityDescriptor; + MapError: Integer; + + function CreateLocalEvent(Idx: Integer; InitialState: Boolean): THandle; + begin + Result := CreateEvent(@sa, true, InitialState, PChar(MonitorHookNames[Idx])); + if Result = 0 then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end; + + + function OpenLocalEvent(Idx: Integer): THandle; + begin + Result := OpenEvent(EVENT_ALL_ACCESS, true, PChar(MonitorHookNames[Idx])); + if Result = 0 then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end; + +begin + InitializeSecurityDescriptor(@Sd,SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(@Sd,true,nil,false); + Sa.nLength := SizeOf(Sa); + Sa.lpSecurityDescriptor := @Sd; + Sa.bInheritHandle := true; + + FAppSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, + 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '01')); + + MapError:=GetLastError; + if MapError= ERROR_ALREADY_EXISTS then + begin + FAppSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '01')); + if (FAppSharedBuf = 0) then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end else + begin + FWriteLock := CreateMutex(@sa, False, PChar(MonitorHookNames[0])); + FWriteEvent := CreateLocalEvent(2, False); + FWriteFinishedEvent := CreateLocalEvent(3, True); + FReadEvent := CreateLocalEvent(4, False); + FReadFinishedEvent := CreateLocalEvent(5, False); + end; + + FDBSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, + 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '02')); + + MapError := GetLastError(); + if MapError = ERROR_ALREADY_EXISTS then + begin + FDBSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '02')); + if (FDBSharedBuf = 0) then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end; + + FMsgSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, + 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '03')); + + MapError := GetLastError; + if MapError = ERROR_ALREADY_EXISTS then + begin + FMsgSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '03')); + if (FMsgSharedBuf = 0) then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end; + + FSQLSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, + 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '04')); + + MapError := GetLastError; + if MapError = ERROR_ALREADY_EXISTS then + begin + FSQLSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '04')); + if (FSQLSharedBuf = 0) then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end; + + FErrSharedBuf := CreateFileMapping(INVALID_HANDLE_VALUE, @sa, PAGE_READWRITE, + 0, cMonitorHookSize, PChar(MonitorHookNames[1] + '05')); + + MapError := GetLastError; + if MapError = ERROR_ALREADY_EXISTS then + begin + FErrSharedBuf := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, PChar(MonitorHookNames[1] + '05')); + if (FErrSharedBuf = 0) then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); + end; + +// FBuffer := MapViewOfFile(FSharedBuffer, FILE_MAP_ALL_ACCESS, 0, 0, 0); + FAppBuffer := MapViewOfFile(FAppSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); + FDBBuffer := MapViewOfFile(FDBSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); + FMsgBuffer := MapViewOfFile(FMsgSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); + FSQLBuffer := MapViewOfFile(FSQLSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); + FErrBuffer := MapViewOfFile(FErrSharedBuf, FILE_MAP_ALL_ACCESS, 0, 0, 0); + + if FAppBuffer = nil then + MonError('Cannot create shared resource. (Windows error %d)',[GetLastError]); +// FMonitorCount := PInteger(FBuffer + cMonitorHookSize - SizeOf(Integer)); + FMonitorCount := PInteger(FAppBuffer + cMonitorHookSize - SizeOf(Integer)); + FReaderCount := PInteger(PAnsiChar(FMonitorCount) - SizeOf(Integer)); + FTraceDataType:= PInteger(PAnsiChar(FMonitorCount) - 2*SizeOf(Integer)); + FExecOK := PInteger(PAnsiChar(FMonitorCount) - 3*SizeOf(Integer)); +// FBufferSize := PInteger(PChar(FMonitorCount) - 3*SizeOf(Integer)); + FAppBufSize := PInteger(PAnsiChar(FMonitorCount) - 4*SizeOf(Integer)); + FDBBufSize := PInteger(PAnsiChar(FMonitorCount) - 5*SizeOf(Integer)); + FMsgBufSize := PInteger(PAnsiChar(FMonitorCount) - 6*SizeOf(Integer)); + FSQLBufSize := PInteger(PAnsiChar(FMonitorCount) - 7*SizeOf(Integer)); + FErrBufSize := PInteger(PAnsiChar(FMonitorCount) - 8*SizeOf(Integer)); + FQPrepareReaderCount:=PInteger(PAnsiChar(FMonitorCount) - 9*SizeOf(Integer)); + FQExecuteReaderCount:=PInteger(PAnsiChar(FMonitorCount) - 10*SizeOf(Integer)); + FQFetchReaderCount :=PInteger(PAnsiChar(FMonitorCount) - 11*SizeOf(Integer)); + FConnectReaderCount :=PInteger(PAnsiChar(FMonitorCount) - 12*SizeOf(Integer)); + FTransactReaderCount:=PInteger(PAnsiChar(FMonitorCount) - 13*SizeOf(Integer)); + FTimeStamp := PDateTime(PAnsiChar(FTransactReaderCount)- SizeOf(TDateTime)); + FReserved := PByte(PAnsiChar(FTimeStamp)- SizeOf(Byte)); + FReserved1 := PByte(PAnsiChar(FReserved )- SizeOf(Byte)); + if MapError= ERROR_ALREADY_EXISTS then + begin + FWriteLock := OpenMutex(MUTEX_ALL_ACCESS, False, PChar(MonitorHookNames[0])); + FWriteEvent := OpenLocalEvent(2); + FWriteFinishedEvent := OpenLocalEvent(3); + FReadEvent := OpenLocalEvent(4); + FReadFinishedEvent := OpenLocalEvent(5); + end else + begin + FMonitorCount^ :=0; + FReaderCount^ :=0; +// FBufferSize^ :=0; + FMsgBufSize^ :=0; + FQPrepareReaderCount^:=0; + FQExecuteReaderCount^:=0; + FQFetchReaderCount^ :=0; + FConnectReaderCount^ :=0; + FTransactReaderCount^:=0; + end; + if FMonitorCount^ < 0 then + FMonitorCount^ := 0; + if FReaderCount^ < 0 then + FReaderCount^ := 0; + vEventsCreated := true; +end; +{$ELSE} +begin +//no support for non-Windows environment yet +end; +{$ENDIF} + +function TPSQLMonitorHook.SQLString(k:integer):Byte; +begin + Result := 127 + k - k; +end; + +procedure TPSQLMonitorHook.DBConnect(db: TNativeConnect; const AExecOK: boolean); +{var + st : String;} +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FConnectReaderCount^>0) then + begin +// st := db.DBOptions.DatabaseName + ': [Connect]'; {do not localize} + WriteSQLData(db.DBOptions.DatabaseName, 'Connect', '', tfConnect, AExecOK, db.GetErrorText); + end; +end; + +procedure TPSQLMonitorHook.DBDisconnect(db: TNativeConnect); +{var + st: String;} +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FConnectReaderCount^>0) then + begin +// st := db.DBOptions.DatabaseName + ': [Disconnect]'; {do not localize} + WriteSQLData(db.DBOptions.DatabaseName, 'Disconnect', '', tfConnect, True); + end; +end; + +destructor TPSQLMonitorHook.Destroy; +begin +{$IFDEF MSWINDOWS} + if vEventsCreated then + begin +// UnmapViewOfFile(FBuffer); + UnmapViewOfFile(FAppBuffer); + UnmapViewOfFile(FDBBuffer); + UnmapViewOfFile(FMsgBuffer); + UnmapViewOfFile(FSQLBuffer); + UnmapViewOfFile(FErrBuffer); + CloseHandle(FAppSharedBuf); + CloseHandle(FDBSharedBuf); + CloseHandle(FMsgSharedBuf); + CloseHandle(FSQLSharedBuf); + CloseHandle(FErrSharedBuf); + +// CloseHandle(FSharedBuffer); + CloseHandle(FWriteEvent); + CloseHandle(FWriteFinishedEvent); + CloseHandle(FReadEvent); + CloseHandle(FReadFinishedEvent); + CloseHandle(FWriteLock); + end; +{$ENDIF} + inherited Destroy; +end; + +function TPSQLMonitorHook.GetEnabled: Boolean; +begin + Result := FActive; +end; + +function TPSQLMonitorHook.GetMonitorCount: Integer; +begin + if FMonitorCount = nil then + Result := 0 + else + Result := FMonitorCount^; +end; + +procedure TPSQLMonitorHook.RegisterMonitor(SQLMonitor: TPSQLCustomMonitor); +begin + if not vEventsCreated then + try + CreateEvents; + except + SQLMonitor.Active := false; + end; + if not Assigned(FPSQLReaderThread) then + FPSQLReaderThread := TMonitorReaderThread.Create; + FPSQLReaderThread.AddMonitor(SQLMonitor); +end; + +procedure TPSQLMonitorHook.ReleaseMonitor(Arg: TPSQLCustomMonitor); +begin + FPSQLWriterThread.ReleaseMonitor(Arg.FHWnd); +end; + +procedure TPSQLMonitorHook.SendMisc(Msg: String); +begin + if FActive then + WriteSQLData('', Msg, '', tfMisc, False); +end; + +procedure TPSQLMonitorHook.SetEnabled(const Value: Boolean); +begin + if FActive <> Value then + FActive := Value; + if (not FActive) and (Assigned(FPSQLWriterThread)) then + begin + FPSQLWriterThread.Terminate; + FPSQLWriterThread.WaitFor; + FPSQLWriterThread.Free; + FPSQLWriterThread := nil; + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TPSQLMonitorHook.SQLExecute(qry: TNativeDataset; const AExecOK: boolean); +var + st: string; +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FQExecuteReaderCount^ > 0) + then + begin + if qry.SQLQuery <> '' then + st := qry.SQLQuery + else + st := string(qry.TableName); + + WriteSQLData(qry.Connect.DBOptions.DatabaseName, 'Execute', st, + tfQExecute, AExecOK, qry.Connect.GetErrorText); + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TPSQLMonitorHook.SQLExecute(db: TNativeConnect; const Sql: string; const AExecOK: boolean); +begin + if FActive and bEnabledMonitoring and (GetMonitorCount > 0) and + (FQExecuteReaderCount^ > 0) + then + begin + WriteSQLData(db.DBOptions.DatabaseName, 'Execute', Sql, + tfQExecute, AExecOK, db.GetErrorText); + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TPSQLMonitorHook.SQLFetch(qry: TNativeDataset); +var + st: String; +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FQFetchReaderCount^>0) + then + begin + if qry.SQLQuery <> '' then + st := 'Query' + else + st := string(qry.TableName); + + st := st + ': Row # '+ IntToStr(qry.RecordNumber) + CRLF; + WriteSQLData(qry.Connect.DBOptions.DatabaseName, 'Fetch', st, tfQFetch, True); + end; +end; + +procedure TPSQLMonitorHook.SQLPrepare(qry: TNativeDataset); +var + st: String; +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FQPrepareReaderCount^>0) + then + begin + if qry.SQLQuery <> '' then + st := qry.SQLQuery + else + st := string(qry.TableName); + + WriteSQLData(qry.Connect.DBOptions.DatabaseName, 'Prepare', st, tfQPrepare, True); + end; +end; + +procedure TPSQLMonitorHook.TRCommit(db: TNativeConnect; const AExecOK: boolean); +{var + st: String;} +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FTransactReaderCount^>0) then + begin +// st := db.DBOptions.DatabaseName + ': [Commit (Hard commit)]'; + WriteSQLData(db.DBOptions.DatabaseName, 'Commit (Hard commit)', '', + tfTransact, AExecOK, db.GetErrorText); + end; +end; + +procedure TPSQLMonitorHook.TRRollback(db: TNativeConnect; const AExecOK: boolean); +{var + st: String;} +begin + if FActive and bEnabledMonitoring and (GetMonitorCount>0) + and (FTransactReaderCount^>0) then + begin +// st := db.DBOptions.DatabaseName + ': [Rollback]'; + WriteSQLData(db.DBOptions.DatabaseName, 'Rollback', '', tfTransact, + AExecOK, db.GetErrorText); + end; +end; + +procedure TPSQLMonitorHook.TRStart(db: TNativeConnect; const AExecOK: boolean); +{var + st: String;} +begin + if FActive and bEnabledMonitoring and bEnabledMonitoring and (GetMonitorCount>0) + and (FTransactReaderCount^>0) then + begin +// st := db.DBOptions.DatabaseName + ': [Start transaction]'; +// WriteSQLData(st, tfTransact); + WriteSQLData(db.DBOptions.DatabaseName, 'Start transaction', '', tfTransact, + AExecOK, db.GetErrorText); + end; +end; + +procedure TPSQLMonitorHook.UnregisterMonitor(SQLMonitor: TPSQLCustomMonitor); +begin + FPSQLReaderThread.RemoveMonitor(SQLMonitor); + if FPSQLReaderThread.FMonitors.Count = 0 then + begin + FPSQLReaderThread.Terminate; + if not Assigned(FPSQLWriterThread) then + begin + FPSQLWriterThread := TMonitorWriterThread.Create; + end; + FPSQLWriterThread.WriteSQLData('', '', '', '', tfMisc, True); + FPSQLReaderThread.WaitFor; + FPSQLReaderThread.Free; + FPSQLReaderThread:=nil; + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TPSQLMonitorHook.WriteSQLData(const ADatabase, AMsg, ASQL: string; + DataType: TPSQLTraceFlag; AExecOK: boolean; const AErrorMsg: string); +var + AppName: string; +begin + if not vEventsCreated then + begin + try + CreateEvents; + except + Enabled := false; + Exit; + end; + end; + + {$IFDEF FMX_AVAILABLE} + AppName := ''; //cannot use TApplication.Title 'cause have no idea if it's FMX or VCL + {$ELSE} + AppName := Application.Title; + {$ENDIF} + if not Assigned(FPSQLWriterThread) then + FPSQLWriterThread := TMonitorWriterThread.Create; + + FPSQLWriterThread.WriteSQLData(AppName, ADatabase, AMsg, ASQL, DataType, AExecOK, AErrorMsg); +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TPSQLMonitorHook.TerminateWriteThread; +begin + if Assigned(FPSQLWriterThread) then + begin + FPSQLWriterThread.Free; + FPSQLWriterThread:=nil + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +constructor TMonitorWriterThread.Create; +begin + StopExec := False; + FMonitorMsgs := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + inherited Create(False); + if FMonitorCount^ = 0 then + {$WARNINGS OFF} + Suspend; + {$WARNINGS ON} +end; +//---------------------------------------------------------------------------------------------------------------------- +destructor TMonitorWriterThread.Destroy; +var + Msg:TObject; +begin + {$WARNINGS OFF} + Resume; + {$WARNINGS ON} + + if FMonitorMsgs.Count>0 then + begin + Msg:=FMonitorMsgs[0]; + FMonitorMsgs.Delete(0); + Msg.Free; + end; + + FMonitorMsgs.Free; + FMonitorMsgs := nil;//mi:2006-09-15 + inherited Destroy; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.Execute; +begin +{$IFDEF MSWINDOWS} + while (Assigned(FMonitorMsgs)) and not StopExec do + begin + if (Terminated or bDone) and (FMonitorMsgs.Count = 0) then//mi:2006-09-15 removed from while condition to ensure FMonitorMsgs<>nil + break; + + if (FMonitorCount^ = 0) then + begin + while FMonitorMsgs.Count <> 0 do + FMonitorMsgs.Remove(FMonitorMsgs[0]); + {$WARNINGS OFF} + Suspend; + {$WARNINGS ON} + end + + else + + if FMonitorMsgs.Count <> 0 then + begin + if (TObject(FMonitorMsgs.Items[0]) is TReleaseObject) then + PostMessage(TReleaseObject(FMonitorMsgs.Items[0]).FHandle, CM_RELEASE, 0, 0) + else + begin + if bEnabledMonitoring then + WriteToBuffer() + else + begin + BeginWrite(); + TPSQLTraceObject(FMonitorMsgs[0]).Free; + FMonitorMsgs.Delete(0); + EndWrite(); + end; + end; + end + else + {$WARNINGS OFF} + Suspend; + {$WARNINGS ON} + end; +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.Lock; +begin +{$IFDEF MSWINDOWS} + WaitForSingleObject(FWriteLock, INFINITE); +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.Unlock; +begin +{$IFDEF MSWINDOWS} + ReleaseMutex(FWriteLock); +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.WriteSQLData(const AAppName, ADatabase, AMsg, ASQL: String; + ADataType: TPSQLTraceFlag; AExecOK: boolean; const AErrorMsg: string); +var + mto : TPSQLTraceObject; +begin + if (FMonitorCount^ <> 0) then + begin + mto := TPSQLTraceObject.Create(AAppName, ADatabase, AMsg, ASQL, ADataType, AExecOK, AErrorMsg); + FMonitorMsgs.Add(mto); + + {$WARNINGS OFF} + Resume; + {$WARNINGS ON} + end + else + begin + FreeAndNil(FPSQLWriterThread) + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.BeginWrite; +begin + Lock(); +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.EndWrite; +begin + { + * 1. Wait to end the write until all registered readers have + * started to wait for a write event + * 2. Block all of those waiting for the write to finish. + * 3. Block all of those waiting for all readers to finish. + * 4. Unblock all readers waiting for a write event. + * 5. Wait until all readers have finished reading. + * 6. Now, block all those waiting for a write event. + * 7. Unblock all readers waiting for a write to be finished. + * 8. Unlock the mutex. + } +{$IFDEF MSWINDOWS} + while WaitForSingleObject(FReadEvent, cDefaultTimeout) = WAIT_TIMEOUT do + begin + if FMonitorCount^ > 0 then + InterlockedDecrement(FMonitorCount^); + + if (FReaderCount^ = FMonitorCount^ - 1) or (FMonitorCount^ = 0) then + SetEvent(FReadEvent); + end; + + ResetEvent(FWriteFinishedEvent); + ResetEvent(FReadFinishedEvent); + SetEvent(FWriteEvent); { Let all readers pass through. } + + while WaitForSingleObject(FReadFinishedEvent, cDefaultTimeout) = WAIT_TIMEOUT do + begin + if (FReaderCount^ = 0) or (InterlockedDecrement(FReaderCount^) = 0) then + SetEvent(FReadFinishedEvent); + end; + + ResetEvent(FWriteEvent); + SetEvent(FWriteFinishedEvent); + Unlock(); +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.WriteToBuffer(); +{$IFDEF MSWINDOWS} +//local procedures + procedure _WriteStrToBuf(const S: string; Buf: PAnsiChar; BufSize: PInteger); + var + i, len: Integer; + Text: String; + ptr : {$IFDEF DELPHI_12}PByte{$ELSE}PChar{$ENDIF}; + begin + Text := EmptyStr; + + for i := 1 to length(S) do + begin + if ((S[i] >= #0) and (S[i] <= #9)) + or (S[i] = #$B) + or (S[i] = #$C) + or ((S[i] >= #$E) and (S[i] <= #31)) + then + Text := Text + '#$' + IntToHex(ord(S[i]), 2) + else + Text := Text + S[i]; + end; + + len := Length(Text) * sizeof(Char);//mi:2008-11-12 unicode compatibility + ptr := {$IFDEF DELPHI_12}PByte{$ELSE}PChar{$ENDIF}(Text);//mi:2008-11-12 + + BufSize^ := 0; +// Move(#0, Buf[0], BufSize^); + while (len > 0) do + begin + BufSize^ := Min(len, cMaxBufferSize); +// Move(ptr, Buf, BufSize^); + CopyMemory(pointer(Buf), ptr, BufSize^); + Inc(ptr, cMaxBufferSize); + Dec(len, cMaxBufferSize); + end; + end; + +begin + Lock(); + + try + if FMonitorCount^ = 0 then + begin + FMonitorMsgs.Remove(FMonitorMsgs[0]); + end + else + begin + BeginWrite(); + + try + _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).Application, FAppBuffer, FAppBufSize); + _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).Database, FDBBuffer, FDBBufSize); + _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).Msg, FMsgBuffer, FMsgBufSize); + _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).SQL, FSQLBuffer, FSQLBufSize); + _WriteStrToBuf(TPSQLTraceObject(FMonitorMsgs[0]).ErrorMsg, FErrBuffer, FErrBufSize); + + FTraceDataType^ := Integer(TPSQLTraceObject(FMonitorMsgs[0]).DataType); + FTimeStamp^ := TPSQLTraceObject(FMonitorMsgs[0]).TimeStamp; + FExecOK^ := Ord(TPSQLTraceObject(FMonitorMsgs[0]).ExecutedOK); + finally + EndWrite(); + end; + end; + + if FMonitorMsgs.Count > 0 then + begin + TPSQLTraceObject(FMonitorMsgs[0]).Free(); + FMonitorMsgs.Delete(0); + end; + finally + Unlock(); + end; +end; +{$ELSE} +begin +end; +{$ENDIF} +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorWriterThread.ReleaseMonitor(HWnd: THandle); +begin + FMonitorMsgs.Add(TReleaseObject.Create(HWnd)); +end; + +{ TPSQLTraceObject } + +constructor TPSQLTraceObject.Create(const AAppName, ADatabase, AMsg, ASQL: string; + ADataType: TPSQLTraceFlag; const AExecOK: boolean; const AErrorMsg: string); +begin + FApplication := AAppName; + FDatabase := ADatabase; + FMsg := AMsg; + FSQL := ASQL; + FDataType := ADataType; + FExecutedOK := AExecOK; + FTimeStamp := Now; + FErrorMsg := AErrorMsg; +end; + +{TReleaseObject} + +constructor TReleaseObject.Create(Handle: THandle); +begin + FHandle := Handle; +end; + +{ReaderThread} + +procedure TMonitorReaderThread.AddMonitor(Arg: TPSQLCustomMonitor); +begin +{$IFDEF DELPHI_16} + CS.Enter; +{$ELSE} + EnterCriticalSection(CS); +{$ENDIF} + try + if FMonitors.IndexOf(Arg) < 0 then + FMonitors.Add(Arg); + finally + {$IFDEF DELPHI_16} + CS.Leave; + {$ELSE} + LeaveCriticalSection(CS); + {$ENDIF} + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorReaderThread.BeginRead(); +begin +{$IFDEF MSWINDOWS} + { + * 1. Wait for the "previous" write event to complete. + * 2. Increment the number of readers. + * 3. if the reader count is the number of interested readers, then + * inform the system that all readers are ready. + * 4. Finally, wait for the FWriteEvent to signal. + } + WaitForSingleObject(FWriteFinishedEvent, INFINITE); + InterlockedIncrement(FReaderCount^); + if FReaderCount^ = FMonitorCount^ then + SetEvent(FReadEvent); + WaitForSingleObject(FWriteEvent, INFINITE); +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +constructor TMonitorReaderThread.Create; +begin + inherited Create(true); +{$IFDEF MSWINDOWS} + st := TPSQLTraceObject.Create('', '', '', '', tfMisc, True); + FMonitors := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; + InterlockedIncrement(FMonitorCount^); + if Suspended then + {$WARNINGS OFF} + Resume; + {$WARNINGS ON} +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +destructor TMonitorReaderThread.Destroy; +begin +{$IFDEF MSWINDOWS} + if FMonitorCount^ > 0 then + {$IFDEF DELPHI_16}TInterlocked.Decrement{$ELSE}InterlockedDecrement{$ENDIF}(FMonitorCount^); + FreeAndNil(FMonitors); + FreeAndNil(st); +{$ENDIF} + inherited Destroy; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorReaderThread.EndRead; +begin +{$IFDEF MSWINDOWS} + if InterlockedDecrement(FReaderCount^) = 0 then + begin + ResetEvent(FReadEvent); + SetEvent(FReadFinishedEvent); + end; +{$ENDIF} +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorReaderThread.Execute; +var + i : Integer; + FTemp : TPSQLTraceObject; +begin + while (not Terminated) and (not bDone) do + begin + ReadSQLData(); + + if not IsBlank(st.FMsg) then + for i := 0 to FMonitors.Count - 1 do + begin + FTemp := TPSQLTraceObject.Create(st.Application, st.Database, + st.Msg, st.SQL, st.FDataType, st.ExecutedOK, st.ErrorMsg); +{$IFDEF MSWINDOWS} + PostMessage(TPSQLCustomMonitor(FMonitors[i]).Handle, + WM_SQL_EVENT, + 0, + LPARAM(FTemp)); +{$ENDIF} + end; + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorReaderThread.ReadSQLData(); + function _ReadStr(Buffer : PAnsiDACChar; Len : Cardinal) : string; + begin + {$IFDEF DELPHI_12} + SetString(Result, PChar(Buffer), Len div sizeof(char)); + {$ELSE} + SetString(Result, Buffer, Len); + {$ENDIF} + end; +begin + st.FMsg := ''; + st.FApplication := ''; + st.FDatabase := ''; + st.FSQL := ''; + st.FErrorMsg := ''; + + BeginRead(); + if not bDone then + try + st.FApplication := _ReadStr(FAppBuffer, FAppBufSize^); + st.FDatabase := _ReadStr(FDBBuffer, FDBBufSize^); + st.FMsg := _ReadStr(FMsgBuffer, FMsgBufSize^); + st.FSQL := _ReadStr(FSQLBuffer, FSQLBufSize^); + st.FErrorMsg := _ReadStr(FErrBuffer, FErrBufSize^); + + st.FDataType := TPSQLTraceFlag(FTraceDataType^); + st.FTimeStamp := TDateTime(FTimeStamp^); + st.FExecutedOK := Boolean(FExecOK^); + finally + EndRead(); + end; +end; +//---------------------------------------------------------------------------------------------------------------------- +procedure TMonitorReaderThread.RemoveMonitor(Arg: TPSQLCustomMonitor); +begin +{$IFDEF DELPHI_16} + CS.Enter; +{$ELSE} + EnterCriticalSection(CS); +{$ENDIF} + try + FMonitors.Remove(Arg); + finally + {$IFDEF DELPHI_16} + CS.Leave; + {$ELSE} + LeaveCriticalSection(CS); + {$ENDIF} + end; +end; + +function MonitorHook: TPSQLMonitorHook; +begin + if (_MonitorHook = nil) and (not bDone) then + begin + {$IFDEF DELPHI_16} + CS.Enter; + {$ELSE} + EnterCriticalSection(CS); + {$ENDIF} + + if (_MonitorHook = nil) and (not bDone) then + begin + _MonitorHook := TPSQLMonitorHook.Create; + end; + + {$IFDEF DELPHI_16} + CS.Leave; + {$ELSE} + LeaveCriticalSection(CS); + {$ENDIF} + end; + Result := _MonitorHook +end; + +procedure EnableMonitoring; +begin + bEnabledMonitoring := true; +end; + +procedure DisableMonitoring; +begin + bEnabledMonitoring :=false; +end; + +function MonitoringEnabled: Boolean; +begin + Result := bEnabledMonitoring; +end; + + +initialization +{$IFDEF DELPHI_16} + CS := TCriticalSection.Create; +{$ELSE} + InitializeCriticalSection(CS); +{$ENDIF} + _MonitorHook := nil; + FPSQLWriterThread := nil; + FPSQLReaderThread := nil; + bDone := False; + bEnabledMonitoring := True; + +finalization + try + bDone := True; + + if Assigned(FPSQLWriterThread) then + begin + {$WARNINGS OFF} + FPSQLWriterThread.Resume; + {$WARNINGS ON} + FPSQLWriterThread.Terminate; + FPSQLWriterThread.StopExec := True; + {$IFDEF DELPHI_6} + FPSQLWriterThread.WaitFor; + {$ENDIF} + end; + + if Assigned(FPSQLReaderThread) then + begin + {$WARNINGS OFF} + FPSQLReaderThread.Resume; + {$WARNINGS ON} + FPSQLReaderThread.Terminate; + {$IFDEF DELPHI_6} + FPSQLReaderThread.WaitFor; + {$ENDIF} + end; + + FreeAndNil(_MonitorHook); + + finally + {$IFDEF DELPHI_16} + CS.Free; + {$ELSE} + DeleteCriticalSection(CS); + {$ENDIF} + end; +end. + diff --git a/PSQLNotify.pas b/Source/PSQLNotify.pas similarity index 96% rename from PSQLNotify.pas rename to Source/PSQLNotify.pas index abe4f1f..09d87a6 100644 --- a/PSQLNotify.pas +++ b/Source/PSQLNotify.pas @@ -1,432 +1,432 @@ -{$I PSQLDAC.inc} -unit PSQLNotify; - -{SVN revision: $Id$} - -interface - -{$DEFINE USE_THREAD_POLLING} //coment this directive to use TTimer instead of TThread - - -uses {$IFNDEF USE_THREAD_POLLING}{$IFDEF FMX}FMX.Types{$ELSE}ExtCtrls{$ENDIF},{$ENDIF} - {$IFDEF MSWINDOWS}Windows,{$ENDIF} - Classes, PSQLTypes, PSQLAccess, PSQLDbTables; - -type - TPSQLNotify = class; - - TNotifyThread = class(TThread) - private - FOwner : TPSQLNotify; - FInterval : cardinal; - FActive : boolean; - procedure CheckEvents; - procedure FireEvents; - public - constructor Create(Owner : TPSQLNotify); - procedure Execute; override; - end; - - TPSQLNotifyEvent = procedure (Sender: TObject; Event: string; ProcessID : Integer) of object; - - TPSQLNotifyEventEx = procedure (Sender: TObject; Channel: string; Payload: string; ProcessID : Integer) of object; - - TPSQLNotify = class (TComponent) - private - _Notify, _Payload : string; - _Pid : Integer; - FHandle : hDBIObj; - FActive : Boolean; - FAutoOpen: Boolean; - FListenList: TStrings; -{$IFDEF USE_THREAD_POLLING} - FNotifyThread : TNotifyThread; -{$ELSE} - FTimer: TTimer; -{$ENDIF} - FDatabase: TPSQLDatabase; - FBackupList: TStringList; - FFirstConnect: Boolean; - FNotifyFired: TPSQLNotifyEvent; - FNotifyFiredEx: TPSQLNotifyEventEx; - function GetStoreActive: boolean; - protected - procedure SetActive(Value: Boolean); - function GetInterval: Cardinal; - procedure SetInterval(Value: Cardinal); - procedure SetListenList(Value: TStrings); - procedure SetDatabase(Value: TPSQLDatabase); - procedure ListenProc(Sender: TObject); - procedure CheckEvents; - procedure ListenChange(Sender: TObject); - procedure ListenChanging(Sender: TObject); - procedure CheckActive; - procedure Notification(AComponent: TComponent; Operation: TOperation); override; - procedure Loaded; override; - function Engine : TPSQLEngine; - function CreateHandle : hDBIObj; - property Handle: hDBIObj read FHandle; - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - procedure OpenNotify; - procedure CloseNotify; - procedure ListenTo(Event: string); - procedure SendNotify(Event: string); overload; - procedure SendNotify(Channel: string; Payload: string); overload; - procedure UnlistenTo(Event: string); - procedure UnlistenAll; - published - property Database: TPSQLDatabase read FDatabase write SetDatabase; - property Active: Boolean read FActive write SetActive stored GetStoreActive; - property ListenList: TStrings read FListenList write SetListenList; - property Interval: Cardinal read GetInterval write SetInterval default 250; - property OnNotify: TPSQLNotifyEvent read FNotifyFired write FNotifyFired; - property OnNotifyEx: TPSQLNotifyEventEx read FNotifyFiredEx write FNotifyFiredEx; - end; - - -implementation - -uses SysUtils, DB; - -const LoopDelayStep = 50; - -procedure TNotifyThread.CheckEvents; -begin - with FOwner do - Check(Engine, Engine.CheckEvents(FHandle, _Pid, _Notify, _Payload)); -end; - -constructor TNotifyThread.Create(Owner: TPSQLNotify); -begin - inherited Create(False); - FOwner := Owner; - FInterval := 250; - FActive := False; - FreeOnTerminate := True; -end; - -procedure TNotifyThread.Execute; -var - Start : cardinal; -begin - Start := 0; - repeat - Sleep(LoopDelayStep); //for quick reaction if termination needed - if (GetTickCount() - Start > FInterval) and FActive then - begin - Synchronize(CheckEvents); - if FOwner._Notify > '' then Synchronize(FireEvents); - Start := GetTickCount(); - end; - until Terminated; -end; - - -procedure TNotifyThread.FireEvents; -begin - with FOwner do - begin - if Assigned(FNotifyFired) then - FNotifyFired(Self, _Notify, _Pid); - if Assigned(FNotifyFiredEx) then - FNotifyFiredEx(Self, _Notify, _Payload, _Pid); - end; -end; - -constructor TPSQLNotify.Create(AOwner: TComponent); -var I: integer; -begin - inherited Create(AOwner); - FListenList := TStringList.Create; - with TStringList(FListenList) do - begin - Duplicates := dupIgnore; - OnChange := ListenChange; - OnChanging := ListenChanging; - end; - FBackupList := TStringList.Create; -{$IFDEF USE_THREAD_POLLING} - FNotifyThread := TNotifyThread.Create(self); -{$ELSE} - FTimer := TTimer.Create(Self); - FTimer.Enabled := False; - SetInterval(250); - FTimer.OnTimer := ListenProc; -{$ENDIF} - FActive := False; - FFirstConnect := True; - - if (csDesigning in ComponentState) and Assigned(AOwner) then - for I := AOwner.ComponentCount - 1 downto 0 do - if AOwner.Components[I] is TPSQLDatabase then - begin - Database := AOwner.Components[I] as TPSQLDatabase; - Break; - end; -end; - -destructor TPSQLNotify.Destroy; -begin - CloseNotify; - FListenList.Free; - FBackupList.Free; -{$IFDEF USE_THREAD_POLLING} - FNotifyThread.Terminate; -{$ELSE} - FTimer.Free; -{$ENDIF} - if FHandle <> nil then - Engine.ClosePGNotify(FHandle); - inherited Destroy; -end; - -procedure TPSQLNotify.SetInterval(Value: Cardinal); -begin -{$IFDEF USE_THREAD_POLLING} - FNotifyThread.FInterval := Value; -{$ELSE} - FTimer.Interval := Value; -{$ENDIF} -end; - -function TPSQLNotify.GetInterval; -begin -{$IFDEF USE_THREAD_POLLING} - Result := FNotifyThread.FInterval; -{$ELSE} - Result := FTimer.Interval; -{$ENDIF} -end; - -procedure TPSQLNotify.SetListenList(Value: TStrings); -var - I: Integer; -begin - FListenList.Assign(Value); - for I := 0 to FListenList.Count -1 do - FListenList[I] := Trim(FListenList[I]); -end; - -function TPSQLNotify.GetStoreActive: boolean; -begin - Result := Active - and Assigned(FDatabase) - and ( - (ddoStoreConnected in FDatabase.DesignOptions) - or not (csDesigning in ComponentState) - ); -end; - -procedure TPSQLNotify.SendNotify(Channel, Payload: string); -begin - CheckActive; - Check(Engine, Engine.DoNotifyEx(FHandle, Channel, Payload)); -end; - -procedure TPSQLNotify.SetActive(Value: Boolean); -begin - if FActive <> Value then - if Value then - OpenNotify - else - CloseNotify; -end; - -procedure TPSQLNotify.SetDatabase(Value: TPSQLDatabase); -begin - if FDatabase <> Value then - begin - CloseNotify; - if FDatabase <> nil then FDatabase.RemoveNotify(Self); - FDatabase := Value; - if FDatabase <> nil then FDatabase.AddNotify(Self); - end; -end; - -procedure TPSQLNotify.Notification(AComponent: TComponent; Operation: TOperation); -begin - inherited Notification(AComponent, Operation); - if (AComponent = FDatabase) and (Operation = opRemove) then - begin - CloseNotify; - if FDatabase <> nil then FDatabase.RemoveNotify(Self); - FDatabase := nil; - end; -end; - -procedure TPSQLNotify.ListenChanging(Sender: TObject); -begin - if not Active then Exit; - FBackupList.Text := FListenList.Text; -end; - -procedure TPSQLNotify.ListenChange(Sender: TObject); -var - I: Integer; -begin - if not Active then Exit; - with TStringList(FListenList) do - begin - OnChange := nil; - OnChanging := nil; - end; - try - for I := 0 to FBackupList.Count-1 do - begin - if FListenList.IndexOf(FBackupList[I]) = -1 then - Check(Engine,Engine.UnlistenTo(FHandle, Trim(FBackupList[I]))); - end; - for I := 0 to FListenList.Count-1 do - begin - if FBackupList.IndexOf(FListenList[I])=-1 then - Check(Engine,Engine.ListenTo(fHandle,Trim(FListenList[I]))); - end; - finally - with TStringList(FListenList) do - begin - OnChange := ListenChange; - OnChanging := ListenChanging; - end; - FBackupList.Clear; - end; -end; - -procedure TPSQLNotify.ListenProc(Sender: TObject); -begin - if not Active then -{$IFDEF USE_THREAD_POLLING} - FNotifyThread.fActive := False -{$ELSE} - FTimer.Enabled := False -{$ENDIF} - else - CheckEvents; -end; - -procedure TPSQLNotify.CheckActive; -begin - if not Assigned(FDatabase) then DatabaseError('Property Database not set!'); - if not Active then DatabaseError('TPSQLNotify not in active mode'); -end; - -procedure TPSQLNotify.Loaded; -begin - inherited Loaded; - if FAutoOpen then - begin - FAutoOpen := False; - OpenNotify; - end; -end; - -function TPSQLNotify.Engine: TPSQLEngine; -begin - Result := FDataBase.Engine; -end; - -function TPSQLNotify.CreateHandle: hDBIObj; -var - PObj: phDBIObj; -begin - PObj := @Result; - Check(Engine, Engine.OpenPGNotify(FDatabase.Handle, PObj^)); -end; - -procedure TPSQLNotify.OpenNotify; -var - I: Integer; -begin - if Active then Exit; - if not Assigned(FDatabase) and (csLoading in ComponentState) then - begin - FAutoOpen := True; - Exit; - end; - if not Assigned(FDatabase) then DatabaseError('Property Database not set!'); - if not FDatabase.Connected then FDatabase.Open; - if not Assigned(FHandle) then FHandle := CreateHandle; - for I := 0 to FListenList.Count - 1 do - Check(Engine, Engine.ListenTo(FHandle, FListenList[I])); - FActive := True; -{$IFDEF USE_THREAD_POLLING} - FNotifyThread.FActive := True; -{$ELSE} - FTimer.Enabled := True; -{$ENDIF} -end; - -procedure TPSQLNotify.CloseNotify; -begin - if not Active then Exit; - FActive := False; -{$IFDEF USE_THREAD_POLLING} - FNotifyThread.fActive := False; -{$ELSE} - FTimer.Enabled := False; -{$ENDIF} - Check(Engine, Engine.UnlistenTo(FHandle, '*')); -end; - -procedure TPSQLNotify.ListenTo(Event: string); -begin - CheckActive; - Check(Engine,Engine.ListenTo(FHandle, Trim(Event))); - with TStringList(FListenList) do - begin - OnChange := nil; - OnChanging := nil; - if IndexOf(Event) = -1 then Append(Event); - OnChange := ListenChange; - OnChanging := ListenChanging; - end; -end; - -procedure TPSQLNotify.SendNotify(Event: string); -begin - CheckActive; - Check(Engine,Engine.DoNotify(FHandle, Event)); -end; - -procedure TPSQLNotify.UnlistenAll; -begin - CheckActive; - Check(Engine, Engine.UnlistenTo(FHandle, '*')); - with TStringList(FListenList) do - begin - OnChange := nil; - OnChanging := nil; - Clear; - OnChange := ListenChange; - OnChanging := ListenChanging; - end; -end; - -procedure TPSQLNotify.UnlistenTo(Event: string); -begin - CheckActive; - Check(Engine,Engine.UnlistenTo(FHandle, Trim(Event))); - with TStringList(FListenList) do - begin - OnChange := nil; - OnChanging := nil; - Delete(IndexOf(Event)); - OnChange := ListenChange; - OnChanging := ListenChanging; - end; -end; - -procedure TPSQLNotify.CheckEvents; -begin - CheckActive; - while True do - begin - Check(Engine,Engine.CheckEvents(FHandle, _Pid, _Notify, _Payload)); - if _Notify = '' then Break; - if Assigned(FNotifyFired) then FNotifyFired(Self, _Notify, _Pid); - if Assigned(FNotifyFiredEx) then FNotifyFiredEx(Self, _Notify, _Payload, _Pid); - end; -end; - -end. +{$I PSQLDAC.inc} +unit PSQLNotify; + +{SVN revision: $Id$} + +interface + +{$DEFINE USE_THREAD_POLLING} //coment this directive to use TTimer instead of TThread + + +uses {$IFNDEF USE_THREAD_POLLING}{$IFDEF FMX}FMX.Types{$ELSE}ExtCtrls{$ENDIF},{$ENDIF} + {$IFDEF MSWINDOWS}Windows,{$ENDIF} + Classes, PSQLTypes, PSQLAccess, PSQLDbTables; + +type + TPSQLNotify = class; + + TNotifyThread = class(TThread) + private + FOwner : TPSQLNotify; + FInterval : cardinal; + FActive : boolean; + procedure CheckEvents; + procedure FireEvents; + public + constructor Create(Owner : TPSQLNotify); + procedure Execute; override; + end; + + TPSQLNotifyEvent = procedure (Sender: TObject; Event: string; ProcessID : Integer) of object; + + TPSQLNotifyEventEx = procedure (Sender: TObject; Channel: string; Payload: string; ProcessID : Integer) of object; + + TPSQLNotify = class (TComponent) + private + _Notify, _Payload : string; + _Pid : Integer; + FHandle : hDBIObj; + FActive : Boolean; + FAutoOpen: Boolean; + FListenList: TStrings; +{$IFDEF USE_THREAD_POLLING} + FNotifyThread : TNotifyThread; +{$ELSE} + FTimer: TTimer; +{$ENDIF} + FDatabase: TPSQLDatabase; + FBackupList: TStringList; + FFirstConnect: Boolean; + FNotifyFired: TPSQLNotifyEvent; + FNotifyFiredEx: TPSQLNotifyEventEx; + function GetStoreActive: boolean; + protected + procedure SetActive(Value: Boolean); + function GetInterval: Cardinal; + procedure SetInterval(Value: Cardinal); + procedure SetListenList(Value: TStrings); + procedure SetDatabase(Value: TPSQLDatabase); + procedure ListenProc(Sender: TObject); + procedure CheckEvents; + procedure ListenChange(Sender: TObject); + procedure ListenChanging(Sender: TObject); + procedure CheckActive; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure Loaded; override; + function Engine : TPSQLEngine; + function CreateHandle : hDBIObj; + property Handle: hDBIObj read FHandle; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure OpenNotify; + procedure CloseNotify; + procedure ListenTo(Event: string); + procedure SendNotify(Event: string); overload; + procedure SendNotify(Channel: string; Payload: string); overload; + procedure UnlistenTo(Event: string); + procedure UnlistenAll; + published + property Database: TPSQLDatabase read FDatabase write SetDatabase; + property Active: Boolean read FActive write SetActive stored GetStoreActive; + property ListenList: TStrings read FListenList write SetListenList; + property Interval: Cardinal read GetInterval write SetInterval default 250; + property OnNotify: TPSQLNotifyEvent read FNotifyFired write FNotifyFired; + property OnNotifyEx: TPSQLNotifyEventEx read FNotifyFiredEx write FNotifyFiredEx; + end; + + +implementation + +uses SysUtils, DB; + +const LoopDelayStep = 50; + +procedure TNotifyThread.CheckEvents; +begin + with FOwner do + Check(Engine, Engine.CheckEvents(FHandle, _Pid, _Notify, _Payload)); +end; + +constructor TNotifyThread.Create(Owner: TPSQLNotify); +begin + inherited Create(False); + FOwner := Owner; + FInterval := 250; + FActive := False; + FreeOnTerminate := True; +end; + +procedure TNotifyThread.Execute; +var + Start : cardinal; +begin + Start := 0; + repeat + Sleep(LoopDelayStep); //for quick reaction if termination needed + if (GetTickCount() - Start > FInterval) and FActive then + begin + Synchronize(CheckEvents); + if FOwner._Notify > '' then Synchronize(FireEvents); + Start := GetTickCount(); + end; + until Terminated; +end; + + +procedure TNotifyThread.FireEvents; +begin + with FOwner do + begin + if Assigned(FNotifyFired) then + FNotifyFired(Self, _Notify, _Pid); + if Assigned(FNotifyFiredEx) then + FNotifyFiredEx(Self, _Notify, _Payload, _Pid); + end; +end; + +constructor TPSQLNotify.Create(AOwner: TComponent); +var I: integer; +begin + inherited Create(AOwner); + FListenList := TStringList.Create; + with TStringList(FListenList) do + begin + Duplicates := dupIgnore; + OnChange := ListenChange; + OnChanging := ListenChanging; + end; + FBackupList := TStringList.Create; +{$IFDEF USE_THREAD_POLLING} + FNotifyThread := TNotifyThread.Create(self); +{$ELSE} + FTimer := TTimer.Create(Self); + FTimer.Enabled := False; + SetInterval(250); + FTimer.OnTimer := ListenProc; +{$ENDIF} + FActive := False; + FFirstConnect := True; + + if (csDesigning in ComponentState) and Assigned(AOwner) then + for I := AOwner.ComponentCount - 1 downto 0 do + if AOwner.Components[I] is TPSQLDatabase then + begin + Database := AOwner.Components[I] as TPSQLDatabase; + Break; + end; +end; + +destructor TPSQLNotify.Destroy; +begin + CloseNotify; + FListenList.Free; + FBackupList.Free; +{$IFDEF USE_THREAD_POLLING} + FNotifyThread.Terminate; +{$ELSE} + FTimer.Free; +{$ENDIF} + if FHandle <> nil then + Engine.ClosePGNotify(FHandle); + inherited Destroy; +end; + +procedure TPSQLNotify.SetInterval(Value: Cardinal); +begin +{$IFDEF USE_THREAD_POLLING} + FNotifyThread.FInterval := Value; +{$ELSE} + FTimer.Interval := Value; +{$ENDIF} +end; + +function TPSQLNotify.GetInterval; +begin +{$IFDEF USE_THREAD_POLLING} + Result := FNotifyThread.FInterval; +{$ELSE} + Result := FTimer.Interval; +{$ENDIF} +end; + +procedure TPSQLNotify.SetListenList(Value: TStrings); +var + I: Integer; +begin + FListenList.Assign(Value); + for I := 0 to FListenList.Count -1 do + FListenList[I] := Trim(FListenList[I]); +end; + +function TPSQLNotify.GetStoreActive: boolean; +begin + Result := Active + and Assigned(FDatabase) + and ( + (ddoStoreConnected in FDatabase.DesignOptions) + or not (csDesigning in ComponentState) + ); +end; + +procedure TPSQLNotify.SendNotify(Channel, Payload: string); +begin + CheckActive; + Check(Engine, Engine.DoNotifyEx(FHandle, Channel, Payload)); +end; + +procedure TPSQLNotify.SetActive(Value: Boolean); +begin + if FActive <> Value then + if Value then + OpenNotify + else + CloseNotify; +end; + +procedure TPSQLNotify.SetDatabase(Value: TPSQLDatabase); +begin + if FDatabase <> Value then + begin + CloseNotify; + if FDatabase <> nil then FDatabase.RemoveNotify(Self); + FDatabase := Value; + if FDatabase <> nil then FDatabase.AddNotify(Self); + end; +end; + +procedure TPSQLNotify.Notification(AComponent: TComponent; Operation: TOperation); +begin + inherited Notification(AComponent, Operation); + if (AComponent = FDatabase) and (Operation = opRemove) then + begin + CloseNotify; + if FDatabase <> nil then FDatabase.RemoveNotify(Self); + FDatabase := nil; + end; +end; + +procedure TPSQLNotify.ListenChanging(Sender: TObject); +begin + if not Active then Exit; + FBackupList.Text := FListenList.Text; +end; + +procedure TPSQLNotify.ListenChange(Sender: TObject); +var + I: Integer; +begin + if not Active then Exit; + with TStringList(FListenList) do + begin + OnChange := nil; + OnChanging := nil; + end; + try + for I := 0 to FBackupList.Count-1 do + begin + if FListenList.IndexOf(FBackupList[I]) = -1 then + Check(Engine,Engine.UnlistenTo(FHandle, Trim(FBackupList[I]))); + end; + for I := 0 to FListenList.Count-1 do + begin + if FBackupList.IndexOf(FListenList[I])=-1 then + Check(Engine,Engine.ListenTo(fHandle,Trim(FListenList[I]))); + end; + finally + with TStringList(FListenList) do + begin + OnChange := ListenChange; + OnChanging := ListenChanging; + end; + FBackupList.Clear; + end; +end; + +procedure TPSQLNotify.ListenProc(Sender: TObject); +begin + if not Active then +{$IFDEF USE_THREAD_POLLING} + FNotifyThread.fActive := False +{$ELSE} + FTimer.Enabled := False +{$ENDIF} + else + CheckEvents; +end; + +procedure TPSQLNotify.CheckActive; +begin + if not Assigned(FDatabase) then DatabaseError('Property Database not set!'); + if not Active then DatabaseError('TPSQLNotify not in active mode'); +end; + +procedure TPSQLNotify.Loaded; +begin + inherited Loaded; + if FAutoOpen then + begin + FAutoOpen := False; + OpenNotify; + end; +end; + +function TPSQLNotify.Engine: TPSQLEngine; +begin + Result := FDataBase.Engine; +end; + +function TPSQLNotify.CreateHandle: hDBIObj; +var + PObj: phDBIObj; +begin + PObj := @Result; + Check(Engine, Engine.OpenPGNotify(FDatabase.Handle, PObj^)); +end; + +procedure TPSQLNotify.OpenNotify; +var + I: Integer; +begin + if Active then Exit; + if not Assigned(FDatabase) and (csLoading in ComponentState) then + begin + FAutoOpen := True; + Exit; + end; + if not Assigned(FDatabase) then DatabaseError('Property Database not set!'); + if not FDatabase.Connected then FDatabase.Open; + if not Assigned(FHandle) then FHandle := CreateHandle; + for I := 0 to FListenList.Count - 1 do + Check(Engine, Engine.ListenTo(FHandle, FListenList[I])); + FActive := True; +{$IFDEF USE_THREAD_POLLING} + FNotifyThread.FActive := True; +{$ELSE} + FTimer.Enabled := True; +{$ENDIF} +end; + +procedure TPSQLNotify.CloseNotify; +begin + if not Active then Exit; + FActive := False; +{$IFDEF USE_THREAD_POLLING} + FNotifyThread.fActive := False; +{$ELSE} + FTimer.Enabled := False; +{$ENDIF} + Check(Engine, Engine.UnlistenTo(FHandle, '*')); +end; + +procedure TPSQLNotify.ListenTo(Event: string); +begin + CheckActive; + Check(Engine,Engine.ListenTo(FHandle, Trim(Event))); + with TStringList(FListenList) do + begin + OnChange := nil; + OnChanging := nil; + if IndexOf(Event) = -1 then Append(Event); + OnChange := ListenChange; + OnChanging := ListenChanging; + end; +end; + +procedure TPSQLNotify.SendNotify(Event: string); +begin + CheckActive; + Check(Engine,Engine.DoNotify(FHandle, Event)); +end; + +procedure TPSQLNotify.UnlistenAll; +begin + CheckActive; + Check(Engine, Engine.UnlistenTo(FHandle, '*')); + with TStringList(FListenList) do + begin + OnChange := nil; + OnChanging := nil; + Clear; + OnChange := ListenChange; + OnChanging := ListenChanging; + end; +end; + +procedure TPSQLNotify.UnlistenTo(Event: string); +begin + CheckActive; + Check(Engine,Engine.UnlistenTo(FHandle, Trim(Event))); + with TStringList(FListenList) do + begin + OnChange := nil; + OnChanging := nil; + Delete(IndexOf(Event)); + OnChange := ListenChange; + OnChanging := ListenChanging; + end; +end; + +procedure TPSQLNotify.CheckEvents; +begin + CheckActive; + while True do + begin + Check(Engine,Engine.CheckEvents(FHandle, _Pid, _Notify, _Payload)); + if _Notify = '' then Break; + if Assigned(FNotifyFired) then FNotifyFired(Self, _Notify, _Pid); + if Assigned(FNotifyFiredEx) then FNotifyFiredEx(Self, _Notify, _Payload, _Pid); + end; +end; + +end. diff --git a/PSQLQueryEdit.dfm b/Source/PSQLQueryEdit.dfm similarity index 95% rename from PSQLQueryEdit.dfm rename to Source/PSQLQueryEdit.dfm index b52d250..2efac6a 100644 --- a/PSQLQueryEdit.dfm +++ b/Source/PSQLQueryEdit.dfm @@ -1,217 +1,217 @@ -object SQLEditForm: TSQLEditForm - Left = 297 - Top = 131 - Width = 750 - Height = 447 - Caption = 'SQL Editor' - Color = clBtnFace - Constraints.MinHeight = 200 - Constraints.MinWidth = 300 - ParentFont = True - OldCreateOrder = True - Position = poScreenCenter - OnCreate = FormCreate - OnDestroy = FormDestroy - OnShow = FormShow - PixelsPerInch = 96 - TextHeight = 13 - object MetaInfoSQLSplitter: TSplitter - Left = 129 - Top = 19 - Width = 3 - Height = 355 - Cursor = crHSplit - MinSize = 100 - OnCanResize = MetaInfoSQLSplitterCanResize - OnMoved = MetaInfoSQLSplitterMoved - end - object TopPanel: TPanel - Left = 0 - Top = 0 - Width = 734 - Height = 19 - Align = alTop - BevelOuter = bvNone - TabOrder = 2 - object TableListLabel: TLabel - Left = 3 - Top = 4 - Width = 35 - Height = 13 - Caption = '&Tables:' - FocusControl = TableList - end - object SQLLabel: TLabel - Left = 166 - Top = 3 - Width = 24 - Height = 13 - Caption = '&SQL:' - end - end - object ButtonPanel: TPanel - Left = 0 - Top = 374 - Width = 734 - Height = 35 - Align = alBottom - BevelOuter = bvNone - TabOrder = 0 - object Image1: TImage - Left = 51 - Top = 16 - Width = 10 - Height = 6 - AutoSize = True - Picture.Data = { - 07544269746D6170A6000000424DA60000000000000076000000280000000A00 - 000006000000010004000000000030000000CE0E0000C40E0000100000000000 - 000000000000000080000080000000808000800000008000800080800000C0C0 - C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFF - FF00333333333300000030999999030000003309999033000000333099033300 - 000033330033330000003333333333000000} - Transparent = True - Visible = False - end - object OkButton: TButton - Left = 487 - Top = 5 - Width = 75 - Height = 25 - Anchors = [akRight, akBottom] - Caption = '&OK' - Default = True - ModalResult = 1 - TabOrder = 0 - end - object CancelButton: TButton - Left = 572 - Top = 5 - Width = 75 - Height = 25 - Anchors = [akRight, akBottom] - Cancel = True - Caption = 'Cancel' - ModalResult = 2 - TabOrder = 1 - end - object HelpButton: TButton - Left = 657 - Top = 5 - Width = 75 - Height = 25 - Anchors = [akRight, akBottom] - Caption = '&Help' - TabOrder = 2 - OnClick = HelpButtonClick - end - object chbSystemObjects: TCheckBox - Left = 5 - Top = 8 - Width = 167 - Height = 17 - Caption = 'Sho&w system tables and fields' - TabOrder = 3 - OnClick = chbSystemObjectsClick - end - end - object MetaInfoPanel: TPanel - Left = 0 - Top = 19 - Width = 129 - Height = 355 - Align = alLeft - BevelOuter = bvNone - TabOrder = 1 - object TableFieldsSplitter: TSplitter - Left = 0 - Top = 115 - Width = 129 - Height = 3 - Cursor = crVSplit - Align = alTop - Beveled = True - MinSize = 1 - OnCanResize = TableFieldsSplitterCanResize - end - object TableListPanel: TPanel - Left = 0 - Top = 0 - Width = 129 - Height = 115 - Align = alTop - BevelOuter = bvNone - TabOrder = 0 - object TableList: TListBox - Left = 2 - Top = 0 - Width = 128 - Height = 87 - Anchors = [akLeft, akTop, akRight, akBottom] - ItemHeight = 13 - TabOrder = 0 - OnClick = TableListClick - OnDblClick = AddTableButtonClick - OnEnter = SQLMemoExit - end - object AddTableButton: TButton - Left = 2 - Top = 90 - Width = 126 - Height = 22 - Anchors = [akLeft, akRight, akBottom] - Caption = 'Add T&able to SQL' - TabOrder = 1 - OnClick = AddTableButtonClick - end - end - object FieldsPanel: TPanel - Left = 0 - Top = 118 - Width = 129 - Height = 237 - Align = alClient - BevelOuter = bvNone - TabOrder = 1 - object FieldListLabel: TLabel - Left = 3 - Top = 3 - Width = 30 - Height = 13 - Caption = '&Fields:' - FocusControl = FieldList - end - object FieldList: TListBox - Left = 0 - Top = 19 - Width = 128 - Height = 185 - Anchors = [akLeft, akTop, akRight, akBottom] - ItemHeight = 13 - MultiSelect = True - TabOrder = 0 - OnDblClick = AddFieldButtonClick - OnEnter = SQLMemoExit - end - object AddFieldButton: TButton - Left = 2 - Top = 210 - Width = 126 - Height = 22 - Anchors = [akLeft, akRight, akBottom] - Caption = 'Add F&ield to SQL' - TabOrder = 1 - OnClick = AddFieldButtonClick - end - end - end - object SQLMemoPanel: TPanel - Left = 132 - Top = 19 - Width = 602 - Height = 355 - Align = alClient - BevelOuter = bvNone - TabOrder = 3 - end -end +object SQLEditForm: TSQLEditForm + Left = 297 + Top = 131 + Width = 750 + Height = 447 + Caption = 'SQL Editor' + Color = clBtnFace + Constraints.MinHeight = 200 + Constraints.MinWidth = 300 + ParentFont = True + OldCreateOrder = True + Position = poScreenCenter + OnCreate = FormCreate + OnDestroy = FormDestroy + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object MetaInfoSQLSplitter: TSplitter + Left = 129 + Top = 19 + Width = 3 + Height = 355 + Cursor = crHSplit + MinSize = 100 + OnCanResize = MetaInfoSQLSplitterCanResize + OnMoved = MetaInfoSQLSplitterMoved + end + object TopPanel: TPanel + Left = 0 + Top = 0 + Width = 734 + Height = 19 + Align = alTop + BevelOuter = bvNone + TabOrder = 2 + object TableListLabel: TLabel + Left = 3 + Top = 4 + Width = 35 + Height = 13 + Caption = '&Tables:' + FocusControl = TableList + end + object SQLLabel: TLabel + Left = 166 + Top = 3 + Width = 24 + Height = 13 + Caption = '&SQL:' + end + end + object ButtonPanel: TPanel + Left = 0 + Top = 374 + Width = 734 + Height = 35 + Align = alBottom + BevelOuter = bvNone + TabOrder = 0 + object Image1: TImage + Left = 51 + Top = 16 + Width = 10 + Height = 6 + AutoSize = True + Picture.Data = { + 07544269746D6170A6000000424DA60000000000000076000000280000000A00 + 000006000000010004000000000030000000CE0E0000C40E0000100000000000 + 000000000000000080000080000000808000800000008000800080800000C0C0 + C000808080000000FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFF + FF00333333333300000030999999030000003309999033000000333099033300 + 000033330033330000003333333333000000} + Transparent = True + Visible = False + end + object OkButton: TButton + Left = 487 + Top = 5 + Width = 75 + Height = 25 + Anchors = [akRight, akBottom] + Caption = '&OK' + Default = True + ModalResult = 1 + TabOrder = 0 + end + object CancelButton: TButton + Left = 572 + Top = 5 + Width = 75 + Height = 25 + Anchors = [akRight, akBottom] + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 1 + end + object HelpButton: TButton + Left = 657 + Top = 5 + Width = 75 + Height = 25 + Anchors = [akRight, akBottom] + Caption = '&Help' + TabOrder = 2 + OnClick = HelpButtonClick + end + object chbSystemObjects: TCheckBox + Left = 5 + Top = 8 + Width = 167 + Height = 17 + Caption = 'Sho&w system tables and fields' + TabOrder = 3 + OnClick = chbSystemObjectsClick + end + end + object MetaInfoPanel: TPanel + Left = 0 + Top = 19 + Width = 129 + Height = 355 + Align = alLeft + BevelOuter = bvNone + TabOrder = 1 + object TableFieldsSplitter: TSplitter + Left = 0 + Top = 115 + Width = 129 + Height = 3 + Cursor = crVSplit + Align = alTop + Beveled = True + MinSize = 1 + OnCanResize = TableFieldsSplitterCanResize + end + object TableListPanel: TPanel + Left = 0 + Top = 0 + Width = 129 + Height = 115 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + object TableList: TListBox + Left = 2 + Top = 0 + Width = 128 + Height = 87 + Anchors = [akLeft, akTop, akRight, akBottom] + ItemHeight = 13 + TabOrder = 0 + OnClick = TableListClick + OnDblClick = AddTableButtonClick + OnEnter = SQLMemoExit + end + object AddTableButton: TButton + Left = 2 + Top = 90 + Width = 126 + Height = 22 + Anchors = [akLeft, akRight, akBottom] + Caption = 'Add T&able to SQL' + TabOrder = 1 + OnClick = AddTableButtonClick + end + end + object FieldsPanel: TPanel + Left = 0 + Top = 118 + Width = 129 + Height = 237 + Align = alClient + BevelOuter = bvNone + TabOrder = 1 + object FieldListLabel: TLabel + Left = 3 + Top = 3 + Width = 30 + Height = 13 + Caption = '&Fields:' + FocusControl = FieldList + end + object FieldList: TListBox + Left = 0 + Top = 19 + Width = 128 + Height = 185 + Anchors = [akLeft, akTop, akRight, akBottom] + ItemHeight = 13 + MultiSelect = True + TabOrder = 0 + OnDblClick = AddFieldButtonClick + OnEnter = SQLMemoExit + end + object AddFieldButton: TButton + Left = 2 + Top = 210 + Width = 126 + Height = 22 + Anchors = [akLeft, akRight, akBottom] + Caption = 'Add F&ield to SQL' + TabOrder = 1 + OnClick = AddFieldButtonClick + end + end + end + object SQLMemoPanel: TPanel + Left = 132 + Top = 19 + Width = 602 + Height = 355 + Align = alClient + BevelOuter = bvNone + TabOrder = 3 + end +end diff --git a/PSQLQueryEdit.pas b/Source/PSQLQueryEdit.pas similarity index 96% rename from PSQLQueryEdit.pas rename to Source/PSQLQueryEdit.pas index 8461475..f4a8f07 100644 --- a/PSQLQueryEdit.pas +++ b/Source/PSQLQueryEdit.pas @@ -1,460 +1,460 @@ -{$I PSQLDAC.inc} -{*******************************************************} -{ } -{ Borland Delphi Visual Component Library } -{ Generic SQL Property Editor } -{ } -{ Copyright (c) 1999 Borland Software Corp. } -{ } -{*******************************************************} - -unit PSQLQueryEdit; - -interface - -uses SysUtils, Forms, Classes, Controls, Graphics, - StdCtrls, ExtCtrls {$IFDEF DELPHI_14}, RTTI{$ENDIF}; - - -type - - TGetTableNamesProc = procedure(List: TStrings; SystemTables: Boolean) of object; - TGetFieldNamesProc = procedure(const TableName: string; List: TStrings; SystemTables: Boolean) of Object; - - TSQLEditForm = class(TForm) - OkButton: TButton; - HelpButton: TButton; - CancelButton: TButton; - AddFieldButton: TButton; - AddTableButton: TButton; - SQLLabel: TLabel; - FieldListLabel: TLabel; - TableListLabel: TLabel; - TopPanel: TPanel; - ButtonPanel: TPanel; - FieldsPanel: TPanel; - MetaInfoPanel: TPanel; - TableListPanel: TPanel; - TableFieldsSplitter: TSplitter; - MetaInfoSQLSplitter: TSplitter; - Image1: TImage; - TableList: TListBox; - FieldList: TListBox; - chbSystemObjects: TCheckBox; - SQLMemoPanel: TPanel; - procedure FormShow(Sender: TObject); - procedure HelpButtonClick(Sender: TObject); - procedure TableFieldsSplitterCanResize(Sender: TObject; var NewSize: Integer; - var Accept: Boolean); - procedure MetaInfoSQLSplitterCanResize(Sender: TObject; - var NewSize: Integer; var Accept: Boolean); - procedure MetaInfoSQLSplitterMoved(Sender: TObject); - procedure TableListClick(Sender: TObject); - procedure AddTableButtonClick(Sender: TObject); - procedure AddFieldButtonClick(Sender: TObject); - procedure SQLMemoExit(Sender: TObject); - procedure FormDestroy(Sender: TObject); - procedure SQLMemoEnter(Sender: TObject); - procedure chbSystemObjectsClick(Sender: TObject); - procedure FormCreate(Sender: TObject); - private - SynMemoUsed: boolean; - SynMemo: TComponent; - SynMemoLines: TStrings; - CharHeight: Integer; - FQuoteChar: char; - FStartTable: string; - GetTableNames: TGetTableNamesProc; - GetFieldNames: TGetFieldNamesProc; - SQLCanvas: TControlCanvas; - SQLMemo: TMemo; -{$IFDEF DELPHI_14} - RTTIContext: TRTTIContext; -{$ENDIF} - procedure InsertText(AText: string; AddComma: Boolean = True); - procedure DrawCaretPosIndicator; - procedure PopulateTableList; - procedure PopulateFieldList; - procedure CreateSimpleMemo(aSQL: string); - function GetSelStart: integer; - procedure SetSelStart(const Value: integer); - procedure MemoKeyPress(Sender: TObject; var Key: Char); - protected - property QuoteChar: char read FQuoteChar write FQuoteChar; - property StartTable: string read FStartTable write FStartTable; - property EditorSelStart: integer read GetSelStart write SetSelStart; - end; - -function EditSQL(var SQL: string; AGetTableNames: TGetTableNamesProc; - AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; - -function EditSQL(SQL: TStrings; AGetTableNames: TGetTableNamesProc; - AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; - -function DefaultReqQuoteChar( Name: string): Boolean; - -implementation - -{$R *.dfm} -uses PSQLTypes, Windows; - -const S = ' object SQLMemoPanel: TPanel'+ -' Left = 24'+ -' Top = 16'+ -' Width = 321'+ -' Height = 241'+ -' object SynMemo1: TSynMemo'+ -' Left = 1'+ -' Top = 1'+ -' Width = 319'+ -' Height = 239'+ -' Align = alClient'+ -' Font.Charset = DEFAULT_CHARSET'+ -' Font.Color = clWindowText'+ -' Font.Height = -13'+ -' Font.Name = ''Courier New'''+ -' Font.Style = []'+ -' TabOrder = 0'+ -' Gutter.AutoSize = True'+ -' Gutter.Font.Charset = DEFAULT_CHARSET'+ -' Gutter.Font.Color = clWindowText'+ -' Gutter.Font.Height = -11'+ -' Gutter.Font.Name = ''Courier New'''+ -' Gutter.Font.Style = []'+ -' Gutter.DigitCount = 2'+ -' Gutter.ShowLineNumbers = True'+ -' Highlighter = PgSQLHighlighter'+ -' Options = [eoAutoIndent, eoDragDropEditing, eoEnhanceHomeKey, eoGroupUndo, eoKeepCaretX, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabsToSpaces]'+ -' WantTabs = True'+ -' WordWrap = True'+ -' end'+ -' object PgSQLHighlighter: TSynSQLSyn'+ -' CommentAttri.Foreground = clSilver'+ -' DataTypeAttri.Foreground = clMaroon'+ -' FunctionAttri.Foreground = clNavy'+ -' KeyAttri.Foreground = clNavy'+ -' NumberAttri.Foreground = clGreen'+ -' SQLDialect = sqlPostgres'+ -' Left = 248'+ -' Top = 128'+ -' end'+ -' end'; - -const - SSelect = 'SELECT'; { Do not localize } - SFrom = 'FROM'; { Do not localize } - -{$IFDEF DELPHI_14} -function StringToComponent(Value: string; Component: TComponent): TComponent; -var - StrStream: TStringStream; - BinStream: TMemoryStream; -begin - StrStream := TStringStream.Create(Value); - try - BinStream := TMemoryStream.Create; - try - ObjectTextToBinary(StrStream, BinStream); - BinStream.Seek(0, soFromBeginning); - Result := BinStream.ReadComponent(Component); - finally - BinStream.Free; - end; - finally - StrStream.Free; - end; -end; -{$ENDIF} - -function EditSQL(var SQL: string; AGetTableNames: TGetTableNamesProc; - AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; -{$IFDEF DELPHI_14} -var FClass: TPersistentClass; - T: TRTTIType; -{$ENDIF} -begin - Result := False; - with TSQLEditForm.Create(nil) do - try -{$IFDEF DELPHI_14} - FClass := GetClass('TSynMemo'); - SynMemoUsed := Assigned(FClass); - if SynMemoUsed then - try - StringToComponent(S, SQLMemoPanel); - SynMemo := SQLMemoPanel.FindComponent('SynMemo1'); - if not Assigned(SynMemo) then raise Exception.Create('SynMemo cannot be found'); - T := RTTIContext.GetType(FClass); - SynMemoLines := T.GetProperty('Lines').GetValue(SynMemo).AsObject as TStrings;// GetObjectProp(SynMemo, 'Lines') as TStrings; - SynMemoLines.Text := SQL; - T.GetProperty('OnExit').SetValue(SynMemo, TValue.From(SQLMemoExit)); - T.GetProperty('OnEnter').SetValue(SynMemo, TValue.From(SQLMemoEnter)); - TMemo(SynMemo).OnKeyPress := MemoKeyPress; - except - SynMemoUsed := False; - CreateSimpleMemo(SQL); - end - else -{$ENDIF} - CreateSimpleMemo(SQL); - GetTableNames := AGetTableNames; - GetFieldNames := AGetFieldNames; - StartTable := AStartTblName; - Result := ShowModal = mrOK; - if Result then - SQL := TrimRight(SynMemoLines.Text); - finally - Free; - end; -end; - -function EditSQL(SQL: TStrings; AGetTableNames: TGetTableNamesProc; - AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; -var - SQLText: string; -begin - SQLText := SQL.Text; - Result := EditSQL(SQLText, AGetTableNames, AGetFieldNames, AStartTblName); - if Result then - SQL.Text := SQLText; -end; - -procedure TSQLEditForm.FormShow(Sender: TObject); -begin - TableList.Sorted := True; - HelpContext := 27271; //hcDADOSQLEdit - SQLCanvas := TControlCanvas.Create; - if not SynMemoUsed then - SQLCanvas.Control := SQLMemo - else - SQLCanvas.Control := TControl(SynMemo); - CharHeight := SQLCanvas.TextHeight('0'); - PopulateTableList; -end; - -function TSQLEditForm.GetSelStart: integer; -begin -{$IFDEF DELPHI_14} - if SynMemoUsed then - Result := RTTIContext.GetType(SynMemo.ClassType).GetProperty('SelStart').GetValue(SynMemo).AsInteger - else -{$ENDIF} - Result := SQLMemo.SelStart; -end; - -procedure TSQLEditForm.FormCreate(Sender: TObject); -begin -{$IFDEF DELPHI_14} - RTTIContext := RTTIContext.Create; -{$ENDIF} -end; - -procedure TSQLEditForm.FormDestroy(Sender: TObject); -begin - SQLCanvas.Free; -{$IFDEF DELPHI_14} - RTTIContext.Free; -{$ENDIF} -end; - -procedure TSQLEditForm.HelpButtonClick(Sender: TObject); -begin - Application.HelpContext(HelpContext); -end; - -procedure TSQLEditForm.PopulateTableList; -var - I: integer; -begin - if @GetTableNames = nil then Exit; - GetTableNames(TableList.Items, chbSystemObjects.Checked); - if FStartTable <> '' then - for I := 0 to TableList.Items.Count -1 do - begin - if AnsiCompareStr( FStartTable, TableList.Items[I] ) = 0 then - begin - TableList.ItemIndex := I; - TableListClick(nil); - break; - end; - end; - if (TableList.Items.Count > 0) and (TableList.ItemIndex = -1) then - begin - TableList.ItemIndex := 0; - PopulateFieldList(); - end; -end; - -procedure TSQLEditForm.TableFieldsSplitterCanResize(Sender: TObject; - var NewSize: Integer; var Accept: Boolean); -begin - Accept := (NewSize > 44) and (NewSize < (MetaInfoPanel.Height - 65)); -end; - -procedure TSQLEditForm.MetaInfoSQLSplitterCanResize(Sender: TObject; - var NewSize: Integer; var Accept: Boolean); -begin - Accept := (NewSize > 100) and (NewSize < (ClientWidth - 100)); -end; - -procedure TSQLEditForm.MetaInfoSQLSplitterMoved(Sender: TObject); -begin - SQLLabel.Left := SQLMemoPanel.Left; -end; - -procedure TSQLEditForm.PopulateFieldList; -begin - if @GetFieldNames = nil then Exit; - GetFieldNames(TableList.Items[TableList.ItemIndex], FieldList.Items, chbSystemObjects.Checked); - FieldList.Items.Insert(0, '*'); -end; - -procedure TSQLEditForm.TableListClick(Sender: TObject); -begin - PopulateFieldList; -end; - -procedure TSQLEditForm.InsertText(AText: string; AddComma: Boolean = True); -var - StartSave: Integer; - S: string; -begin - S := SynMemoLines.Text; - StartSave := EditorSelStart; - if (S <> '') and (StartSave > 0) and not CharInSet(S[StartSave], [' ','(']) and - not (AText[1] = ' ') then - begin - if AddComma and (S[StartSave] <> ',') then - AText := ', ' + AText else - AText := ' ' + AText; - end; - System.Insert(AText, S, StartSave + 1); - SynMemoLines.Text := TrimRight(S); - EditorSelStart := StartSave + Length(AText); - if SynMemoUsed then (SynMemo as TControl).Update else SQLMemo.Update; - DrawCaretPosIndicator; -end; - -procedure TSQLEditForm.AddTableButtonClick(Sender: TObject); -begin - if TableList.ItemIndex = -1 then Exit; - if SynMemoLines.Text > '' then - InsertText(TableList.Items[TableList.ItemIndex], False) - else - InsertText('SELECT * FROM ' + TableList.Items[TableList.ItemIndex], False); -end; - -procedure TSQLEditForm.AddFieldButtonClick(Sender: TObject); -var - I: Integer; - ColumnName: string; -begin - if FieldList.ItemIndex = -1 then Exit; - if (SynMemoLines.Text = '') then - begin - SynMemoLines.Text := SSelect; - EditorSelStart := Length(SSelect); - end; - for I := 0 to FieldList.Items.Count - 1 do - if FieldList.Selected[I] then - begin - ColumnName := FieldList.Items[I]; - InsertText(ColumnName, (SynMemoLines.Text <> SSelect) and (ColumnName <> '*')); - end; -end; - -procedure TSQLEditForm.SQLMemoExit(Sender: TObject); -begin - DrawCaretPosIndicator; -end; - -procedure TSQLEditForm.SetSelStart(const Value: integer); -begin -{$IFDEF DELPHI_14} - if SynMemoUsed then - RTTIContext.GetType(SynMemo.ClassType).GetProperty('SelStart').SetValue(SynMemo, Value) - else -{$ENDIF} - SQLMemo.SelStart := Value; -end; - -procedure TSQLEditForm.SQLMemoEnter(Sender: TObject); -begin - (Sender as TControl).Invalidate; // Erase the CaretPos indicator -end; - -procedure TSQLEditForm.DrawCaretPosIndicator; -var - Pos: TPoint; -{$IFDEF DELPHI_14} - T: TRTTIType; - V: TValue; - P: TRTTIProperty; - M: TRTTIMethod; -{$ENDIF} -begin -{$IFDEF DELPHI_14} - if SynMemoUsed then - begin - T := RTTIContext.GetType(SynMemo.ClassType); - P := T.GetProperty('DisplayXY'); - M := T.GetMethod('RowColumnToPixels'); - V := P.GetValue(SynMemo); - V := M.Invoke(SynMemo, [V]); - Pos := V.AsType; - Inc(Pos.Y, CharHeight); - end - else -{$ENDIF} - begin - Pos.Y := (SQLMemo.CaretPos.Y + 1) * CharHeight; - Pos.X := SQLCanvas.TextWidth(Copy(SQLMemo.Lines[Pos.Y], 1, Pos.X)) - 3 ; - end; - SQLCanvas.Draw(Pos.X, Pos.Y, Image1.Picture.Graphic); -end; - -function DefaultReqQuoteChar(Name: string): Boolean; -var - p: PChar; -begin - p := PChar(Name); - Result := False; - repeat - if not CharInSet(p^, ['a'..'z', '0'..'9', '_']) then - begin - Result := True; - break; - end; - Inc(p) - until p^ = #0; -end; - -procedure TSQLEditForm.chbSystemObjectsClick(Sender: TObject); -begin - PopulateTableList(); -end; - -procedure TSQLEditForm.MemoKeyPress(Sender: TObject; var Key: Char); -begin - if Key = ^A then - begin - TMemo(Sender).SelectAll; - Key := #0; - end; -end; - -procedure TSQLEditForm.CreateSimpleMemo(aSQL: string); -begin - SQLMemo := TMemo.Create(Self); - SQLMemo.Name := 'mmSQL'; - SQLMemo.Parent := Self; - SQLMemo.Align := alClient; - SQLMemo.Lines.Text := aSQL; - SQLMemo.OnEnter := SQLMemoEnter; - SQLMemo.OnExit := SQLMemoExit; - SQLMemo.ScrollBars := ssBoth; - SQLMemo.OnKeyPress := MemoKeyPress; - SynMemoLines := SQLMemo.Lines; -end; - -end. +{$I PSQLDAC.inc} +{*******************************************************} +{ } +{ Borland Delphi Visual Component Library } +{ Generic SQL Property Editor } +{ } +{ Copyright (c) 1999 Borland Software Corp. } +{ } +{*******************************************************} + +unit PSQLQueryEdit; + +interface + +uses SysUtils, Forms, Classes, Controls, Graphics, + StdCtrls, ExtCtrls {$IFDEF DELPHI_14}, RTTI{$ENDIF}; + + +type + + TGetTableNamesProc = procedure(List: TStrings; SystemTables: Boolean) of object; + TGetFieldNamesProc = procedure(const TableName: string; List: TStrings; SystemTables: Boolean) of Object; + + TSQLEditForm = class(TForm) + OkButton: TButton; + HelpButton: TButton; + CancelButton: TButton; + AddFieldButton: TButton; + AddTableButton: TButton; + SQLLabel: TLabel; + FieldListLabel: TLabel; + TableListLabel: TLabel; + TopPanel: TPanel; + ButtonPanel: TPanel; + FieldsPanel: TPanel; + MetaInfoPanel: TPanel; + TableListPanel: TPanel; + TableFieldsSplitter: TSplitter; + MetaInfoSQLSplitter: TSplitter; + Image1: TImage; + TableList: TListBox; + FieldList: TListBox; + chbSystemObjects: TCheckBox; + SQLMemoPanel: TPanel; + procedure FormShow(Sender: TObject); + procedure HelpButtonClick(Sender: TObject); + procedure TableFieldsSplitterCanResize(Sender: TObject; var NewSize: Integer; + var Accept: Boolean); + procedure MetaInfoSQLSplitterCanResize(Sender: TObject; + var NewSize: Integer; var Accept: Boolean); + procedure MetaInfoSQLSplitterMoved(Sender: TObject); + procedure TableListClick(Sender: TObject); + procedure AddTableButtonClick(Sender: TObject); + procedure AddFieldButtonClick(Sender: TObject); + procedure SQLMemoExit(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure SQLMemoEnter(Sender: TObject); + procedure chbSystemObjectsClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + private + SynMemoUsed: boolean; + SynMemo: TComponent; + SynMemoLines: TStrings; + CharHeight: Integer; + FQuoteChar: char; + FStartTable: string; + GetTableNames: TGetTableNamesProc; + GetFieldNames: TGetFieldNamesProc; + SQLCanvas: TControlCanvas; + SQLMemo: TMemo; +{$IFDEF DELPHI_14} + RTTIContext: TRTTIContext; +{$ENDIF} + procedure InsertText(AText: string; AddComma: Boolean = True); + procedure DrawCaretPosIndicator; + procedure PopulateTableList; + procedure PopulateFieldList; + procedure CreateSimpleMemo(aSQL: string); + function GetSelStart: integer; + procedure SetSelStart(const Value: integer); + procedure MemoKeyPress(Sender: TObject; var Key: Char); + protected + property QuoteChar: char read FQuoteChar write FQuoteChar; + property StartTable: string read FStartTable write FStartTable; + property EditorSelStart: integer read GetSelStart write SetSelStart; + end; + +function EditSQL(var SQL: string; AGetTableNames: TGetTableNamesProc; + AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; + +function EditSQL(SQL: TStrings; AGetTableNames: TGetTableNamesProc; + AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; + +function DefaultReqQuoteChar( Name: string): Boolean; + +implementation + +{$R *.dfm} +uses PSQLTypes, Windows; + +const S = ' object SQLMemoPanel: TPanel'+ +' Left = 24'+ +' Top = 16'+ +' Width = 321'+ +' Height = 241'+ +' object SynMemo1: TSynMemo'+ +' Left = 1'+ +' Top = 1'+ +' Width = 319'+ +' Height = 239'+ +' Align = alClient'+ +' Font.Charset = DEFAULT_CHARSET'+ +' Font.Color = clWindowText'+ +' Font.Height = -13'+ +' Font.Name = ''Courier New'''+ +' Font.Style = []'+ +' TabOrder = 0'+ +' Gutter.AutoSize = True'+ +' Gutter.Font.Charset = DEFAULT_CHARSET'+ +' Gutter.Font.Color = clWindowText'+ +' Gutter.Font.Height = -11'+ +' Gutter.Font.Name = ''Courier New'''+ +' Gutter.Font.Style = []'+ +' Gutter.DigitCount = 2'+ +' Gutter.ShowLineNumbers = True'+ +' Highlighter = PgSQLHighlighter'+ +' Options = [eoAutoIndent, eoDragDropEditing, eoEnhanceHomeKey, eoGroupUndo, eoKeepCaretX, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabsToSpaces]'+ +' WantTabs = True'+ +' WordWrap = True'+ +' end'+ +' object PgSQLHighlighter: TSynSQLSyn'+ +' CommentAttri.Foreground = clSilver'+ +' DataTypeAttri.Foreground = clMaroon'+ +' FunctionAttri.Foreground = clNavy'+ +' KeyAttri.Foreground = clNavy'+ +' NumberAttri.Foreground = clGreen'+ +' SQLDialect = sqlPostgres'+ +' Left = 248'+ +' Top = 128'+ +' end'+ +' end'; + +const + SSelect = 'SELECT'; { Do not localize } + SFrom = 'FROM'; { Do not localize } + +{$IFDEF DELPHI_14} +function StringToComponent(Value: string; Component: TComponent): TComponent; +var + StrStream: TStringStream; + BinStream: TMemoryStream; +begin + StrStream := TStringStream.Create(Value); + try + BinStream := TMemoryStream.Create; + try + ObjectTextToBinary(StrStream, BinStream); + BinStream.Seek(0, soFromBeginning); + Result := BinStream.ReadComponent(Component); + finally + BinStream.Free; + end; + finally + StrStream.Free; + end; +end; +{$ENDIF} + +function EditSQL(var SQL: string; AGetTableNames: TGetTableNamesProc; + AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; +{$IFDEF DELPHI_14} +var FClass: TPersistentClass; + T: TRTTIType; +{$ENDIF} +begin + Result := False; + with TSQLEditForm.Create(nil) do + try +{$IFDEF DELPHI_14} + FClass := GetClass('TSynMemo'); + SynMemoUsed := Assigned(FClass); + if SynMemoUsed then + try + StringToComponent(S, SQLMemoPanel); + SynMemo := SQLMemoPanel.FindComponent('SynMemo1'); + if not Assigned(SynMemo) then raise Exception.Create('SynMemo cannot be found'); + T := RTTIContext.GetType(FClass); + SynMemoLines := T.GetProperty('Lines').GetValue(SynMemo).AsObject as TStrings;// GetObjectProp(SynMemo, 'Lines') as TStrings; + SynMemoLines.Text := SQL; + T.GetProperty('OnExit').SetValue(SynMemo, TValue.From(SQLMemoExit)); + T.GetProperty('OnEnter').SetValue(SynMemo, TValue.From(SQLMemoEnter)); + TMemo(SynMemo).OnKeyPress := MemoKeyPress; + except + SynMemoUsed := False; + CreateSimpleMemo(SQL); + end + else +{$ENDIF} + CreateSimpleMemo(SQL); + GetTableNames := AGetTableNames; + GetFieldNames := AGetFieldNames; + StartTable := AStartTblName; + Result := ShowModal = mrOK; + if Result then + SQL := TrimRight(SynMemoLines.Text); + finally + Free; + end; +end; + +function EditSQL(SQL: TStrings; AGetTableNames: TGetTableNamesProc; + AGetFieldNames: TGetFieldNamesProc; AStartTblName : string = ''): Boolean; overload; +var + SQLText: string; +begin + SQLText := SQL.Text; + Result := EditSQL(SQLText, AGetTableNames, AGetFieldNames, AStartTblName); + if Result then + SQL.Text := SQLText; +end; + +procedure TSQLEditForm.FormShow(Sender: TObject); +begin + TableList.Sorted := True; + HelpContext := 27271; //hcDADOSQLEdit + SQLCanvas := TControlCanvas.Create; + if not SynMemoUsed then + SQLCanvas.Control := SQLMemo + else + SQLCanvas.Control := TControl(SynMemo); + CharHeight := SQLCanvas.TextHeight('0'); + PopulateTableList; +end; + +function TSQLEditForm.GetSelStart: integer; +begin +{$IFDEF DELPHI_14} + if SynMemoUsed then + Result := RTTIContext.GetType(SynMemo.ClassType).GetProperty('SelStart').GetValue(SynMemo).AsInteger + else +{$ENDIF} + Result := SQLMemo.SelStart; +end; + +procedure TSQLEditForm.FormCreate(Sender: TObject); +begin +{$IFDEF DELPHI_14} + RTTIContext := RTTIContext.Create; +{$ENDIF} +end; + +procedure TSQLEditForm.FormDestroy(Sender: TObject); +begin + SQLCanvas.Free; +{$IFDEF DELPHI_14} + RTTIContext.Free; +{$ENDIF} +end; + +procedure TSQLEditForm.HelpButtonClick(Sender: TObject); +begin + Application.HelpContext(HelpContext); +end; + +procedure TSQLEditForm.PopulateTableList; +var + I: integer; +begin + if @GetTableNames = nil then Exit; + GetTableNames(TableList.Items, chbSystemObjects.Checked); + if FStartTable <> '' then + for I := 0 to TableList.Items.Count -1 do + begin + if AnsiCompareStr( FStartTable, TableList.Items[I] ) = 0 then + begin + TableList.ItemIndex := I; + TableListClick(nil); + break; + end; + end; + if (TableList.Items.Count > 0) and (TableList.ItemIndex = -1) then + begin + TableList.ItemIndex := 0; + PopulateFieldList(); + end; +end; + +procedure TSQLEditForm.TableFieldsSplitterCanResize(Sender: TObject; + var NewSize: Integer; var Accept: Boolean); +begin + Accept := (NewSize > 44) and (NewSize < (MetaInfoPanel.Height - 65)); +end; + +procedure TSQLEditForm.MetaInfoSQLSplitterCanResize(Sender: TObject; + var NewSize: Integer; var Accept: Boolean); +begin + Accept := (NewSize > 100) and (NewSize < (ClientWidth - 100)); +end; + +procedure TSQLEditForm.MetaInfoSQLSplitterMoved(Sender: TObject); +begin + SQLLabel.Left := SQLMemoPanel.Left; +end; + +procedure TSQLEditForm.PopulateFieldList; +begin + if @GetFieldNames = nil then Exit; + GetFieldNames(TableList.Items[TableList.ItemIndex], FieldList.Items, chbSystemObjects.Checked); + FieldList.Items.Insert(0, '*'); +end; + +procedure TSQLEditForm.TableListClick(Sender: TObject); +begin + PopulateFieldList; +end; + +procedure TSQLEditForm.InsertText(AText: string; AddComma: Boolean = True); +var + StartSave: Integer; + S: string; +begin + S := SynMemoLines.Text; + StartSave := EditorSelStart; + if (S <> '') and (StartSave > 0) and not CharInSet(S[StartSave], [' ','(']) and + not (AText[1] = ' ') then + begin + if AddComma and (S[StartSave] <> ',') then + AText := ', ' + AText else + AText := ' ' + AText; + end; + System.Insert(AText, S, StartSave + 1); + SynMemoLines.Text := TrimRight(S); + EditorSelStart := StartSave + Length(AText); + if SynMemoUsed then (SynMemo as TControl).Update else SQLMemo.Update; + DrawCaretPosIndicator; +end; + +procedure TSQLEditForm.AddTableButtonClick(Sender: TObject); +begin + if TableList.ItemIndex = -1 then Exit; + if SynMemoLines.Text > '' then + InsertText(TableList.Items[TableList.ItemIndex], False) + else + InsertText('SELECT * FROM ' + TableList.Items[TableList.ItemIndex], False); +end; + +procedure TSQLEditForm.AddFieldButtonClick(Sender: TObject); +var + I: Integer; + ColumnName: string; +begin + if FieldList.ItemIndex = -1 then Exit; + if (SynMemoLines.Text = '') then + begin + SynMemoLines.Text := SSelect; + EditorSelStart := Length(SSelect); + end; + for I := 0 to FieldList.Items.Count - 1 do + if FieldList.Selected[I] then + begin + ColumnName := FieldList.Items[I]; + InsertText(ColumnName, (SynMemoLines.Text <> SSelect) and (ColumnName <> '*')); + end; +end; + +procedure TSQLEditForm.SQLMemoExit(Sender: TObject); +begin + DrawCaretPosIndicator; +end; + +procedure TSQLEditForm.SetSelStart(const Value: integer); +begin +{$IFDEF DELPHI_14} + if SynMemoUsed then + RTTIContext.GetType(SynMemo.ClassType).GetProperty('SelStart').SetValue(SynMemo, Value) + else +{$ENDIF} + SQLMemo.SelStart := Value; +end; + +procedure TSQLEditForm.SQLMemoEnter(Sender: TObject); +begin + (Sender as TControl).Invalidate; // Erase the CaretPos indicator +end; + +procedure TSQLEditForm.DrawCaretPosIndicator; +var + Pos: TPoint; +{$IFDEF DELPHI_14} + T: TRTTIType; + V: TValue; + P: TRTTIProperty; + M: TRTTIMethod; +{$ENDIF} +begin +{$IFDEF DELPHI_14} + if SynMemoUsed then + begin + T := RTTIContext.GetType(SynMemo.ClassType); + P := T.GetProperty('DisplayXY'); + M := T.GetMethod('RowColumnToPixels'); + V := P.GetValue(SynMemo); + V := M.Invoke(SynMemo, [V]); + Pos := V.AsType; + Inc(Pos.Y, CharHeight); + end + else +{$ENDIF} + begin + Pos.Y := (SQLMemo.CaretPos.Y + 1) * CharHeight; + Pos.X := SQLCanvas.TextWidth(Copy(SQLMemo.Lines[Pos.Y], 1, Pos.X)) - 3 ; + end; + SQLCanvas.Draw(Pos.X, Pos.Y, Image1.Picture.Graphic); +end; + +function DefaultReqQuoteChar(Name: string): Boolean; +var + p: PChar; +begin + p := PChar(Name); + Result := False; + repeat + if not CharInSet(p^, ['a'..'z', '0'..'9', '_']) then + begin + Result := True; + break; + end; + Inc(p) + until p^ = #0; +end; + +procedure TSQLEditForm.chbSystemObjectsClick(Sender: TObject); +begin + PopulateTableList(); +end; + +procedure TSQLEditForm.MemoKeyPress(Sender: TObject; var Key: Char); +begin + if Key = ^A then + begin + TMemo(Sender).SelectAll; + Key := #0; + end; +end; + +procedure TSQLEditForm.CreateSimpleMemo(aSQL: string); +begin + SQLMemo := TMemo.Create(Self); + SQLMemo.Name := 'mmSQL'; + SQLMemo.Parent := Self; + SQLMemo.Align := alClient; + SQLMemo.Lines.Text := aSQL; + SQLMemo.OnEnter := SQLMemoEnter; + SQLMemo.OnExit := SQLMemoExit; + SQLMemo.ScrollBars := ssBoth; + SQLMemo.OnKeyPress := MemoKeyPress; + SynMemoLines := SQLMemo.Lines; +end; + +end. diff --git a/PSQLStoredProcFrm.dfm b/Source/PSQLStoredProcFrm.dfm similarity index 95% rename from PSQLStoredProcFrm.dfm rename to Source/PSQLStoredProcFrm.dfm index 8993bfe..413ff15 100644 --- a/PSQLStoredProcFrm.dfm +++ b/Source/PSQLStoredProcFrm.dfm @@ -1,86 +1,86 @@ -object PSQLStoredProcProp: TPSQLStoredProcProp - Left = 245 - Top = 145 - Width = 525 - Height = 295 - Caption = 'TPSQLStoredProc Editor...' - Color = clBtnFace - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - OldCreateOrder = False - Position = poDesktopCenter - DesignSize = ( - 517 - 261) - PixelsPerInch = 96 - TextHeight = 13 - object Panel1: TPanel - Left = 0 - Top = 0 - Width = 517 - Height = 25 - Align = alTop - Alignment = taLeftJustify - Caption = ' PostgreSQL Stored Procedure Options' - Color = clGray - Font.Charset = DEFAULT_CHARSET - Font.Color = clWhite - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [fsBold] - ParentFont = False - TabOrder = 0 - end - object OkBtn: TButton - Left = 328 - Top = 231 - Width = 75 - Height = 25 - Anchors = [akLeft, akBottom] - Caption = 'OK' - ModalResult = 1 - TabOrder = 1 - end - object CancelBtn: TButton - Left = 412 - Top = 231 - Width = 75 - Height = 25 - Anchors = [akLeft, akBottom] - Caption = 'Cancel' - ModalResult = 2 - TabOrder = 2 - end - object ListView1: TListView - Left = 0 - Top = 25 - Width = 517 - Height = 192 - Align = alTop - Columns = < - item - AutoSize = True - Caption = 'Full Name' - end - item - Caption = 'Overload ID' - Width = 60 - end - item - AutoSize = True - Caption = 'Arguments' - end> - GridLines = True - HotTrack = True - HotTrackStyles = [htHandPoint, htUnderlineHot] - RowSelect = True - TabOrder = 3 - ViewStyle = vsReport - OnColumnClick = ListView1ColumnClick - OnCompare = ListView1Compare - OnSelectItem = ListView1SelectItem - end -end +object PSQLStoredProcProp: TPSQLStoredProcProp + Left = 245 + Top = 145 + Width = 525 + Height = 295 + Caption = 'TPSQLStoredProc Editor...' + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + OldCreateOrder = False + Position = poDesktopCenter + DesignSize = ( + 517 + 261) + PixelsPerInch = 96 + TextHeight = 13 + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 517 + Height = 25 + Align = alTop + Alignment = taLeftJustify + Caption = ' PostgreSQL Stored Procedure Options' + Color = clGray + Font.Charset = DEFAULT_CHARSET + Font.Color = clWhite + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [fsBold] + ParentFont = False + TabOrder = 0 + end + object OkBtn: TButton + Left = 328 + Top = 231 + Width = 75 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = 'OK' + ModalResult = 1 + TabOrder = 1 + end + object CancelBtn: TButton + Left = 412 + Top = 231 + Width = 75 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 2 + end + object ListView1: TListView + Left = 0 + Top = 25 + Width = 517 + Height = 192 + Align = alTop + Columns = < + item + AutoSize = True + Caption = 'Full Name' + end + item + Caption = 'Overload ID' + Width = 60 + end + item + AutoSize = True + Caption = 'Arguments' + end> + GridLines = True + HotTrack = True + HotTrackStyles = [htHandPoint, htUnderlineHot] + RowSelect = True + TabOrder = 3 + ViewStyle = vsReport + OnColumnClick = ListView1ColumnClick + OnCompare = ListView1Compare + OnSelectItem = ListView1SelectItem + end +end diff --git a/PSQLStoredProcFrm.pas b/Source/PSQLStoredProcFrm.pas similarity index 96% rename from PSQLStoredProcFrm.pas rename to Source/PSQLStoredProcFrm.pas index 1ced310..bb8a9f8 100644 --- a/PSQLStoredProcFrm.pas +++ b/Source/PSQLStoredProcFrm.pas @@ -1,273 +1,273 @@ -{$I PSQLdac.inc} -unit PSQLStoredProcFrm; - -{SVN revision: $Id$} - -interface - -uses - Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, - Dialogs, StdCtrls, ExtCtrls, ComCtrls, PSQLDBTables; - -type - TPSQLStoredProcProp = class(TForm) - Panel1: TPanel; - OkBtn: TButton; - CancelBtn: TButton; - ListView1: TListView; - procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn); - procedure ListView1Compare(Sender: TObject; Item1, Item2: TListItem; - Data: Integer; var Compare: Integer); - procedure ListView1SelectItem(Sender: TObject; Item: TListItem; - Selected: Boolean); - private - FColumnToSort: integer; - FColumnsSortDir: array[0..2] of boolean; - public - FStoredProc: TPSQLStoredProc; - function Edit: Boolean; - procedure SetStoredProcProperty; - procedure GetStoredProcList; - end; - -var - PSQLStoredProcProp: TPSQLStoredProcProp; - PB: TProgressBar; -function EditStoredProc(AStoredProc: TPSQLStoredProc): Boolean; - -implementation - -uses PSQLTypes; - -{$R *.dfm} - -function GetSQLForParams(const anIntVer: integer): string; - const ArgNames: array[boolean] of string = ('NULL','proargnames[g.s]'); - - sqlShowParameters810 = 'SELECT proargnames[g.s], '+ - ' COALESCE(proargtypes[g.s-1], proallargtypes[g.s]),'+ - ' proargmodes[g.s], '+ - ' format_type(t.oid,-1) '+ - ' FROM '+ - ' pg_proc p, '+ - ' pg_type t , '+ - ' generate_series(1,32) as g(s)'+ - ' WHERE '+ - ' COALESCE(p.proargtypes[g.s-1],proallargtypes[g.s]) = t.oid AND '; - - - sqlShowParameters = 'SELECT %s, '+ //proargnames[g.s] > 8.0.0 - ' proargtypes[g.s], '+ - ' ''i''::varchar, '+ - ' format_type(t.oid,-1) '+ - ' FROM '+ - ' pg_proc p, '+ - ' pg_type t , '+ - ' %s as g(s) '+ //!!! generate_series(0,current_setting(''max_function_args'')::int) - ' WHERE '+ - ' p.proargtypes[g.s] = t.oid AND '; - - sqlTail = ' p.oid = %d '+ - ' ORDER BY g.s '; - function GetGenSeries(const StartPos,EndPos: string): string; - const sqlGenerateSeries = - '(select i*10+j as n'+ - ' from (select 0 union all select 1 union all select 2 union all'+ - ' select 3 union all select 4 union all select 5 union all'+ - ' select 6 union all select 7 union all select 8 union all'+ - ' select 9) s1(i),'+ - ' (select 0 union all select 1 union all select 2 union all'+ - ' select 3 union all select 4 union all select 5 union all'+ - ' select 6 union all select 7 union all select 8 union all'+ - ' select 9) s2(j)'+ - ' where (i*10+j >= %s) AND (i*10+j <= %s))'; - begin - if anIntVer >= 080000 then - Result := Format('generate_series(%s,%s)',[StartPos, EndPos]) - else - Result := Format(sqlGenerateSeries,[StartPos, EndPos]); - end; -begin - If anIntVer >= 080100 then - Result := sqlShowParameters810 + sqlTail - else - Result := Format(sqlShowParameters,[ArgNames[anIntVer >= 080000], GetGenSeries('0','32')]) + - sqlTail; -end; - -function EditStoredProc(AStoredProc: TPSQLStoredProc): Boolean; -begin - If not Assigned(AStoredProc) - or not Assigned(AStoredProc.Database) - or not AStoredProc.Database.Connected - then - raise EPSQLDatabaseError.CreateFmt('Can''t open stored procedure editor due '+ - 'one of possible errors:'#13#10+ - '1. Stored procedure object not assigned'#13#10+ - '2. Database property of stored procedure object not assigned'#13#10+ - '3. Database object specified in Database property of stored procedure object'+ - ' is not Connected',[]); - with TPSQLStoredProcProp.Create(Application) do - try - FStoredProc := AStoredProc; - Result := Edit; - finally - Free; - end; -end; - -function TPSQLStoredProcProp.Edit: Boolean; -var - Splash: TForm; - Pan: Tpanel; -begin - Splash := TForm.Create(Application); -try - Splash.Width := 300; - Splash.Height := 75; - Splash.Position := poDesktopCenter; - Splash.BorderStyle := bsNone; - Splash.Font.Style := [fsBold]; - Pan := TPanel.Create(Splash); - Pan.Parent := Splash; - Pan.Align := alClient; - Pan.BevelOuter := bvRaised; - PAn.BevelWidth := 2; - Pan.BevelInner := bvLowered; - with TLabel.Create(Pan) do - begin - Parent := Pan; - Caption := 'Loading Procedures List...'; - AutoSize := true; - Left := (Splash.Width - Width) div 2; - Top := Height; - end; - PB := TProgressBar.Create(Pan); - PB.Parent := Pan; - PB.Left := 10; - PB.Top := (Splash.Height - PB.Height) div 2; - PB.Width := Splash.Width-PB.Left-10; - PB.Smooth := True; - Splash.Show; - Application.ProcessMessages; - GetStoredProcList; -finally - Splash.Free; -end; - FColumnsSortDir[0] := False; - FColumnToSort := 0; - ListView1.AlphaSort; - Result := False; - if ShowModal = mrOk then - begin - SetStoredProcProperty; - Result := True; - end; -end; - -procedure TPSQLStoredProcProp.SetStoredProcProperty; -begin - FStoredProc.StoredProcName := ListView1.Selected.Caption; - FStoredProc.Overload := strtoint(ListView1.Selected.SubItems[0]); -end; - -procedure TPSQLStoredProcProp.ListView1ColumnClick(Sender: TObject; - Column: TListColumn); -begin - FColumnToSort := Column.Index; - FColumnsSortDir[FColumnToSort] := not FColumnsSortDir[FColumnToSort]; - (Sender as TCustomListView).AlphaSort; -end; - -procedure TPSQLStoredProcProp.ListView1Compare(Sender: TObject; Item1, - Item2: TListItem; Data: Integer; var Compare: Integer); -begin - case FColumnToSort of - 0: - begin - Compare := CompareText(Item1.Caption,Item2.Caption); - If FColumnsSortDir[0] then Compare := -1*Compare; - end; - 1: - begin - Compare := (strtoint(Item1.SubItems[0]) - strtoint(Item2.SubItems[0])); - If FColumnsSortDir[1] then Compare := -1*Compare; - end; - else - begin - Compare := CompareText(Item1.SubItems[1],Item2.SubItems[1]); - If FColumnsSortDir[2] then Compare := -1*Compare; - end; - end; -end; - -procedure TPSQLStoredProcProp.ListView1SelectItem(Sender: TObject; - Item: TListItem; Selected: Boolean); -begin - Okbtn.Enabled := Selected = True; -end; - -procedure TPSQLStoredProcProp.GetStoredProcList; -Var SL: TStringList; - i: integer; - LI: TlistItem; - ParamString: string; - Q: TPSQLQuery; - OldCursor: TCursor; - SQL: string; -begin - OldCursor := Screen.Cursor; - Sl := TStringList.Create; - try - Screen.Cursor := crSQLWait; - Application.ProcessMessages; //added to defroze - FStoredProc.Database.GetStoredProcNames('',SL); - {$WARNINGS OFF} //make D5 compiler happy - Q := TPSQLQuery.Create(nil); - {$WARNINGS ON} - try - Q.Database := FStoredProc.Database; - Q.RequestLive := False; - Q.ParamCheck := False; - Q.Options := [dsoOIDAsInt]; - ListView1.Items.BeginUpdate; - ListView1.Items.Clear; - PB.Min := 0; - PB.Max := Sl.Count-1; - SQL := GetSQLForParams(FStoredProc.Database.ServerVersionAsInt); - for i:=0 to Sl.Count-1 do - begin - LI := ListView1.Items.Add; - LI.Caption := SL[I]; - LI.SubItems.Append(inttostr(integer(SL.Objects[I]))); - Q.SQL.Text := Format(SQL,[integer(SL.Objects[I])]); - Q.Open; - Q.First; - ParamString := ''; - while not Q.Eof do - begin - if not Q.Fields[2].IsNull then - case Q.Fields[2].AsString[1] of - 'o': ParamString := ParamString + 'OUT '; - 'b': ParamString := ParamString + 'INOUT '; - end; - If Q.Fields[0].AsString > '' then - ParamString := ParamString + Q.Fields[0].AsString + ' '; - ParamString := ParamString + Q.Fields[3].AsString + '; '; - Q.Next; - end; - LI.SubItems.Append('('+Copy(ParamString,1,Length(ParamString)-2)+')'); - PB.Position := i; - Application.ProcessMessages; - end; - ListView1.Items.EndUpdate; - finally - Q.Free; - end; - finally - Sl.Free; - Screen.Cursor := OldCursor; - end; -end; - -end. +{$I PSQLdac.inc} +unit PSQLStoredProcFrm; + +{SVN revision: $Id$} + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, + Dialogs, StdCtrls, ExtCtrls, ComCtrls, PSQLDBTables; + +type + TPSQLStoredProcProp = class(TForm) + Panel1: TPanel; + OkBtn: TButton; + CancelBtn: TButton; + ListView1: TListView; + procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn); + procedure ListView1Compare(Sender: TObject; Item1, Item2: TListItem; + Data: Integer; var Compare: Integer); + procedure ListView1SelectItem(Sender: TObject; Item: TListItem; + Selected: Boolean); + private + FColumnToSort: integer; + FColumnsSortDir: array[0..2] of boolean; + public + FStoredProc: TPSQLStoredProc; + function Edit: Boolean; + procedure SetStoredProcProperty; + procedure GetStoredProcList; + end; + +var + PSQLStoredProcProp: TPSQLStoredProcProp; + PB: TProgressBar; +function EditStoredProc(AStoredProc: TPSQLStoredProc): Boolean; + +implementation + +uses PSQLTypes; + +{$R *.dfm} + +function GetSQLForParams(const anIntVer: integer): string; + const ArgNames: array[boolean] of string = ('NULL','proargnames[g.s]'); + + sqlShowParameters810 = 'SELECT proargnames[g.s], '+ + ' COALESCE(proargtypes[g.s-1], proallargtypes[g.s]),'+ + ' proargmodes[g.s], '+ + ' format_type(t.oid,-1) '+ + ' FROM '+ + ' pg_proc p, '+ + ' pg_type t , '+ + ' generate_series(1,32) as g(s)'+ + ' WHERE '+ + ' COALESCE(p.proargtypes[g.s-1],proallargtypes[g.s]) = t.oid AND '; + + + sqlShowParameters = 'SELECT %s, '+ //proargnames[g.s] > 8.0.0 + ' proargtypes[g.s], '+ + ' ''i''::varchar, '+ + ' format_type(t.oid,-1) '+ + ' FROM '+ + ' pg_proc p, '+ + ' pg_type t , '+ + ' %s as g(s) '+ //!!! generate_series(0,current_setting(''max_function_args'')::int) + ' WHERE '+ + ' p.proargtypes[g.s] = t.oid AND '; + + sqlTail = ' p.oid = %d '+ + ' ORDER BY g.s '; + function GetGenSeries(const StartPos,EndPos: string): string; + const sqlGenerateSeries = + '(select i*10+j as n'+ + ' from (select 0 union all select 1 union all select 2 union all'+ + ' select 3 union all select 4 union all select 5 union all'+ + ' select 6 union all select 7 union all select 8 union all'+ + ' select 9) s1(i),'+ + ' (select 0 union all select 1 union all select 2 union all'+ + ' select 3 union all select 4 union all select 5 union all'+ + ' select 6 union all select 7 union all select 8 union all'+ + ' select 9) s2(j)'+ + ' where (i*10+j >= %s) AND (i*10+j <= %s))'; + begin + if anIntVer >= 080000 then + Result := Format('generate_series(%s,%s)',[StartPos, EndPos]) + else + Result := Format(sqlGenerateSeries,[StartPos, EndPos]); + end; +begin + If anIntVer >= 080100 then + Result := sqlShowParameters810 + sqlTail + else + Result := Format(sqlShowParameters,[ArgNames[anIntVer >= 080000], GetGenSeries('0','32')]) + + sqlTail; +end; + +function EditStoredProc(AStoredProc: TPSQLStoredProc): Boolean; +begin + If not Assigned(AStoredProc) + or not Assigned(AStoredProc.Database) + or not AStoredProc.Database.Connected + then + raise EPSQLDatabaseError.CreateFmt('Can''t open stored procedure editor due '+ + 'one of possible errors:'#13#10+ + '1. Stored procedure object not assigned'#13#10+ + '2. Database property of stored procedure object not assigned'#13#10+ + '3. Database object specified in Database property of stored procedure object'+ + ' is not Connected',[]); + with TPSQLStoredProcProp.Create(Application) do + try + FStoredProc := AStoredProc; + Result := Edit; + finally + Free; + end; +end; + +function TPSQLStoredProcProp.Edit: Boolean; +var + Splash: TForm; + Pan: Tpanel; +begin + Splash := TForm.Create(Application); +try + Splash.Width := 300; + Splash.Height := 75; + Splash.Position := poDesktopCenter; + Splash.BorderStyle := bsNone; + Splash.Font.Style := [fsBold]; + Pan := TPanel.Create(Splash); + Pan.Parent := Splash; + Pan.Align := alClient; + Pan.BevelOuter := bvRaised; + PAn.BevelWidth := 2; + Pan.BevelInner := bvLowered; + with TLabel.Create(Pan) do + begin + Parent := Pan; + Caption := 'Loading Procedures List...'; + AutoSize := true; + Left := (Splash.Width - Width) div 2; + Top := Height; + end; + PB := TProgressBar.Create(Pan); + PB.Parent := Pan; + PB.Left := 10; + PB.Top := (Splash.Height - PB.Height) div 2; + PB.Width := Splash.Width-PB.Left-10; + PB.Smooth := True; + Splash.Show; + Application.ProcessMessages; + GetStoredProcList; +finally + Splash.Free; +end; + FColumnsSortDir[0] := False; + FColumnToSort := 0; + ListView1.AlphaSort; + Result := False; + if ShowModal = mrOk then + begin + SetStoredProcProperty; + Result := True; + end; +end; + +procedure TPSQLStoredProcProp.SetStoredProcProperty; +begin + FStoredProc.StoredProcName := ListView1.Selected.Caption; + FStoredProc.Overload := strtoint(ListView1.Selected.SubItems[0]); +end; + +procedure TPSQLStoredProcProp.ListView1ColumnClick(Sender: TObject; + Column: TListColumn); +begin + FColumnToSort := Column.Index; + FColumnsSortDir[FColumnToSort] := not FColumnsSortDir[FColumnToSort]; + (Sender as TCustomListView).AlphaSort; +end; + +procedure TPSQLStoredProcProp.ListView1Compare(Sender: TObject; Item1, + Item2: TListItem; Data: Integer; var Compare: Integer); +begin + case FColumnToSort of + 0: + begin + Compare := CompareText(Item1.Caption,Item2.Caption); + If FColumnsSortDir[0] then Compare := -1*Compare; + end; + 1: + begin + Compare := (strtoint(Item1.SubItems[0]) - strtoint(Item2.SubItems[0])); + If FColumnsSortDir[1] then Compare := -1*Compare; + end; + else + begin + Compare := CompareText(Item1.SubItems[1],Item2.SubItems[1]); + If FColumnsSortDir[2] then Compare := -1*Compare; + end; + end; +end; + +procedure TPSQLStoredProcProp.ListView1SelectItem(Sender: TObject; + Item: TListItem; Selected: Boolean); +begin + Okbtn.Enabled := Selected = True; +end; + +procedure TPSQLStoredProcProp.GetStoredProcList; +Var SL: TStringList; + i: integer; + LI: TlistItem; + ParamString: string; + Q: TPSQLQuery; + OldCursor: TCursor; + SQL: string; +begin + OldCursor := Screen.Cursor; + Sl := TStringList.Create; + try + Screen.Cursor := crSQLWait; + Application.ProcessMessages; //added to defroze + FStoredProc.Database.GetStoredProcNames('',SL); + {$WARNINGS OFF} //make D5 compiler happy + Q := TPSQLQuery.Create(nil); + {$WARNINGS ON} + try + Q.Database := FStoredProc.Database; + Q.RequestLive := False; + Q.ParamCheck := False; + Q.Options := [dsoOIDAsInt]; + ListView1.Items.BeginUpdate; + ListView1.Items.Clear; + PB.Min := 0; + PB.Max := Sl.Count-1; + SQL := GetSQLForParams(FStoredProc.Database.ServerVersionAsInt); + for i:=0 to Sl.Count-1 do + begin + LI := ListView1.Items.Add; + LI.Caption := SL[I]; + LI.SubItems.Append(inttostr(integer(SL.Objects[I]))); + Q.SQL.Text := Format(SQL,[integer(SL.Objects[I])]); + Q.Open; + Q.First; + ParamString := ''; + while not Q.Eof do + begin + if not Q.Fields[2].IsNull then + case Q.Fields[2].AsString[1] of + 'o': ParamString := ParamString + 'OUT '; + 'b': ParamString := ParamString + 'INOUT '; + end; + If Q.Fields[0].AsString > '' then + ParamString := ParamString + Q.Fields[0].AsString + ' '; + ParamString := ParamString + Q.Fields[3].AsString + '; '; + Q.Next; + end; + LI.SubItems.Append('('+Copy(ParamString,1,Length(ParamString)-2)+')'); + PB.Position := i; + Application.ProcessMessages; + end; + ListView1.Items.EndUpdate; + finally + Q.Free; + end; + finally + Sl.Free; + Screen.Cursor := OldCursor; + end; +end; + +end. diff --git a/PSQLTools.pas b/Source/PSQLTools.pas similarity index 96% rename from PSQLTools.pas rename to Source/PSQLTools.pas index 36c2ce1..146ca32 100644 --- a/PSQLTools.pas +++ b/Source/PSQLTools.pas @@ -1,214 +1,214 @@ -{$I pSQLDAC.inc} -unit PSQLTools; - -{SVN revision: $Id$} - -interface - -uses {$IFDEF FPC}LCLIntf,{$ENDIF} SysUtils, Classes, PSQLDbTables, PSQLAccess, PSQLTypes; - -type - EPSQLToolsException = class(Exception); - - TPSQLOperation = (poANALYZE, poCLUSTER, poVACUUM, poREINDEX); - - TVacuumOption = (voFULL, voFREEZE, voANALYZE); - TVacuumOptions = set of TVacuumOption; - - TPSQLTools = class; - - TToolsEvent = procedure(Sender: TPSQLTools; const Operation: TPSQLOperation) of object; - - TPSQLTools = class(TComponent) - private - FAbout : TPSQLDACAbout; - {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; - FQuery : TPSQLQuery; - FColumnList : TStrings; - FOnError : TToolsEvent; - FOnSuccess : TToolsEvent; - FPSQLOperation : TPSQLOperation; - FVacuumOptions : TVacuumOptions; - FVerbose : boolean; - FIndexName: string; - FTableName: string; - procedure SetDatabase(const Value: TPSQLDatabase); - procedure SetColumnList(const Value: TStrings); - function AnalyzeStmt: string; - function ClusterStmt: string; - function ReindexStmt: string; - function VacuumStmt: string; - protected - procedure Notification(AComponent: TComponent; Operation: TOperation ); Override; - public - constructor Create(AOwner : TComponent); override; - destructor Destroy; override; - function Execute: Boolean; overload; - procedure Execute(Operation: TPSQLOperation); overload; - published - property About : TPSQLDACAbout read FAbout write FAbout; - property ColumnList : TStrings read FColumnList write SetColumnList; - property Database : TPSQLDatabase read FDatabase write SetDatabase; - property IndexName : string read FIndexName write FIndexName; - property Operation : TPSQLOperation read FPSQLOperation write FPSQLOperation default poANALYZE; - property TableName : string read FTableName write FTableName; - property VacuumOptions : TVacuumOptions read FVacuumOptions write FVacuumOptions; - property Verbose : boolean read FVerbose write FVerbose default False; - property OnError : TToolsEvent read FOnError write FOnError; - property OnSuccess : TToolsEvent read FOnSuccess write FOnSuccess; - end; - - -implementation - -{ TPSQLTools } - -function TPSQLTools.AnalyzeStmt: string; -var Target: string; - i: integer; -begin - if FTableName > '' then - Target := FTableName; - if (FColumnList.Count > 0) and (Target > '') then - begin - Target := Target + ' (' + FColumnList[0]; - for i := 1 to FColumnList.Count-1 do - Target := Target + ', ' + FColumnList[i]; - Target := Target + ')'; - end; - if FVerbose then - Result := Format('ANALYZE VERBOSE %s',[Target]) - else - Result := Format('ANALYZE %s',[Target]); -end; - -function TPSQLTools.ClusterStmt: string; -var Target: string; -begin - if FTableName > '' then - Target := FTableName; - if (FIndexName > '') and (Target > '') then - Target := FIndexName + ' ON ' + Target; - Result := Format('CLUSTER %s',[Target]); -end; - -constructor TPSQLTools.Create(AOwner: TComponent); -var I: integer; -begin - inherited Create(AOwner); - {$WARNINGS OFF} //make D5 compiler happy - FQuery := TPSQLQuery.Create(nil); - {$WARNINGS ON} - FColumnList := TStringList.Create; - FVacuumOptions := []; - if (csDesigning in ComponentState) and Assigned(Owner) then - for I := Owner.ComponentCount - 1 downto 0 do - if Owner.Components[I] is TPSQLDatabase then - begin - Database := Owner.Components[I] as TPSQLDatabase; - Break; - end; -end; - -destructor TPSQLTools.Destroy; -begin - FQuery.Free; - FColumnList.Free; - inherited; -end; - -procedure TPSQLTools.Execute(Operation: TPSQLOperation); -begin - case Operation of - poANALYZE: FDatabase.Execute(AnalyzeStmt); - poCLUSTER: FDatabase.Execute(ClusterStmt); - poVACUUM: begin - if FDatabase.TransactionStatus in [trstINTRANS, trstINERROR] then - FDatabase.Commit; - FDatabase.Execute(VacuumStmt); - end; - poREINDEX: FDatabase.Execute(ReindexStmt); - end; - if Assigned(OnSuccess) then - FOnSuccess(Self, Operation); -end; - -function TPSQLTools.Execute: Boolean; -begin - Result := False; - case FPSQLOperation of - poANALYZE: FQuery.SQL.Text := AnalyzeStmt; - poCLUSTER: FQuery.SQL.Text := ClusterStmt; - poVACUUM: with FQuery.SQL do - begin - if FDatabase.TransactionStatus in [trstINTRANS, trstINERROR] then - FDatabase.Commit; - FQuery.SQL.Text := VacuumStmt; - end; - poREINDEX: FQuery.SQL.Text := ReindexStmt; - end; - try - FQuery.ExecSQL; - Result := True; - if Assigned(OnSuccess) then - FOnSuccess(Self, FPSQLOperation); - except - if Assigned(OnError) then - FOnError(Self, FPSQLOperation); - end; -end; - -procedure TPSQLTools.Notification(AComponent: TComponent; - Operation: TOperation); -begin - inherited Notification( AComponent, Operation ); - if (Operation = opRemove) and (AComponent = FDatabase) then - FDatabase := nil; -end; - -function TPSQLTools.ReindexStmt: string; -var Target: string; -begin - if FIndexName > '' then - Target := 'INDEX ' + FIndexName - else - if FTableName > '' then - Target := 'TABLE ' + TableName - else - if Assigned(FDatabase) and (FDatabase.DatabaseName > '') then - Target := 'DATABASE ' + AnsiQuotedStr(FDatabase.DatabaseName,'"'); - if Target = '' then - raise EPSQLToolsException.Create('Reindex target not assigned') - else - Result := Format('REINDEX %s',[Target]); -end; - -procedure TPSQLTools.SetColumnList(const Value: TStrings); -begin - FColumnList.Assign(Value); -end; - -procedure TPSQLTools.SetDatabase(const Value: TPSQLDatabase); -begin - if Value <> FDatabase then - begin - FDatabase := Value; - FQuery.Database := FDatabase; - end; -end; - -function TPSQLTools.VacuumStmt: string; -var Target: string; -begin - if voFULL in FVacuumOptions then - Target := 'FULL'; - if voFREEZE in FVacuumOptions then - Target := Target + ' FREEZE'; - if FVerbose then - Target := Target + ' VERBOSE'; - if voAnalyze in FVacuumOptions then - Target := Target + ' ANALYZE'; - Result := Format('VACUUM %s %s',[Target, FTableName]); -end; - -end. +{$I pSQLDAC.inc} +unit PSQLTools; + +{SVN revision: $Id$} + +interface + +uses {$IFDEF FPC}LCLIntf,{$ENDIF} SysUtils, Classes, PSQLDbTables, PSQLAccess, PSQLTypes; + +type + EPSQLToolsException = class(Exception); + + TPSQLOperation = (poANALYZE, poCLUSTER, poVACUUM, poREINDEX); + + TVacuumOption = (voFULL, voFREEZE, voANALYZE); + TVacuumOptions = set of TVacuumOption; + + TPSQLTools = class; + + TToolsEvent = procedure(Sender: TPSQLTools; const Operation: TPSQLOperation) of object; + + TPSQLTools = class(TComponent) + private + FAbout : TPSQLDACAbout; + {$IFDEF NEXTGEN}[Weak]{$ENDIF} FDatabase: TPSQLDatabase; + FQuery : TPSQLQuery; + FColumnList : TStrings; + FOnError : TToolsEvent; + FOnSuccess : TToolsEvent; + FPSQLOperation : TPSQLOperation; + FVacuumOptions : TVacuumOptions; + FVerbose : boolean; + FIndexName: string; + FTableName: string; + procedure SetDatabase(const Value: TPSQLDatabase); + procedure SetColumnList(const Value: TStrings); + function AnalyzeStmt: string; + function ClusterStmt: string; + function ReindexStmt: string; + function VacuumStmt: string; + protected + procedure Notification(AComponent: TComponent; Operation: TOperation ); Override; + public + constructor Create(AOwner : TComponent); override; + destructor Destroy; override; + function Execute: Boolean; overload; + procedure Execute(Operation: TPSQLOperation); overload; + published + property About : TPSQLDACAbout read FAbout write FAbout; + property ColumnList : TStrings read FColumnList write SetColumnList; + property Database : TPSQLDatabase read FDatabase write SetDatabase; + property IndexName : string read FIndexName write FIndexName; + property Operation : TPSQLOperation read FPSQLOperation write FPSQLOperation default poANALYZE; + property TableName : string read FTableName write FTableName; + property VacuumOptions : TVacuumOptions read FVacuumOptions write FVacuumOptions; + property Verbose : boolean read FVerbose write FVerbose default False; + property OnError : TToolsEvent read FOnError write FOnError; + property OnSuccess : TToolsEvent read FOnSuccess write FOnSuccess; + end; + + +implementation + +{ TPSQLTools } + +function TPSQLTools.AnalyzeStmt: string; +var Target: string; + i: integer; +begin + if FTableName > '' then + Target := FTableName; + if (FColumnList.Count > 0) and (Target > '') then + begin + Target := Target + ' (' + FColumnList[0]; + for i := 1 to FColumnList.Count-1 do + Target := Target + ', ' + FColumnList[i]; + Target := Target + ')'; + end; + if FVerbose then + Result := Format('ANALYZE VERBOSE %s',[Target]) + else + Result := Format('ANALYZE %s',[Target]); +end; + +function TPSQLTools.ClusterStmt: string; +var Target: string; +begin + if FTableName > '' then + Target := FTableName; + if (FIndexName > '') and (Target > '') then + Target := FIndexName + ' ON ' + Target; + Result := Format('CLUSTER %s',[Target]); +end; + +constructor TPSQLTools.Create(AOwner: TComponent); +var I: integer; +begin + inherited Create(AOwner); + {$WARNINGS OFF} //make D5 compiler happy + FQuery := TPSQLQuery.Create(nil); + {$WARNINGS ON} + FColumnList := TStringList.Create; + FVacuumOptions := []; + if (csDesigning in ComponentState) and Assigned(Owner) then + for I := Owner.ComponentCount - 1 downto 0 do + if Owner.Components[I] is TPSQLDatabase then + begin + Database := Owner.Components[I] as TPSQLDatabase; + Break; + end; +end; + +destructor TPSQLTools.Destroy; +begin + FQuery.Free; + FColumnList.Free; + inherited; +end; + +procedure TPSQLTools.Execute(Operation: TPSQLOperation); +begin + case Operation of + poANALYZE: FDatabase.Execute(AnalyzeStmt); + poCLUSTER: FDatabase.Execute(ClusterStmt); + poVACUUM: begin + if FDatabase.TransactionStatus in [trstINTRANS, trstINERROR] then + FDatabase.Commit; + FDatabase.Execute(VacuumStmt); + end; + poREINDEX: FDatabase.Execute(ReindexStmt); + end; + if Assigned(OnSuccess) then + FOnSuccess(Self, Operation); +end; + +function TPSQLTools.Execute: Boolean; +begin + Result := False; + case FPSQLOperation of + poANALYZE: FQuery.SQL.Text := AnalyzeStmt; + poCLUSTER: FQuery.SQL.Text := ClusterStmt; + poVACUUM: with FQuery.SQL do + begin + if FDatabase.TransactionStatus in [trstINTRANS, trstINERROR] then + FDatabase.Commit; + FQuery.SQL.Text := VacuumStmt; + end; + poREINDEX: FQuery.SQL.Text := ReindexStmt; + end; + try + FQuery.ExecSQL; + Result := True; + if Assigned(OnSuccess) then + FOnSuccess(Self, FPSQLOperation); + except + if Assigned(OnError) then + FOnError(Self, FPSQLOperation); + end; +end; + +procedure TPSQLTools.Notification(AComponent: TComponent; + Operation: TOperation); +begin + inherited Notification( AComponent, Operation ); + if (Operation = opRemove) and (AComponent = FDatabase) then + FDatabase := nil; +end; + +function TPSQLTools.ReindexStmt: string; +var Target: string; +begin + if FIndexName > '' then + Target := 'INDEX ' + FIndexName + else + if FTableName > '' then + Target := 'TABLE ' + TableName + else + if Assigned(FDatabase) and (FDatabase.DatabaseName > '') then + Target := 'DATABASE ' + AnsiQuotedStr(FDatabase.DatabaseName,'"'); + if Target = '' then + raise EPSQLToolsException.Create('Reindex target not assigned') + else + Result := Format('REINDEX %s',[Target]); +end; + +procedure TPSQLTools.SetColumnList(const Value: TStrings); +begin + FColumnList.Assign(Value); +end; + +procedure TPSQLTools.SetDatabase(const Value: TPSQLDatabase); +begin + if Value <> FDatabase then + begin + FDatabase := Value; + FQuery.Database := FDatabase; + end; +end; + +function TPSQLTools.VacuumStmt: string; +var Target: string; +begin + if voFULL in FVacuumOptions then + Target := 'FULL'; + if voFREEZE in FVacuumOptions then + Target := Target + ' FREEZE'; + if FVerbose then + Target := Target + ' VERBOSE'; + if voAnalyze in FVacuumOptions then + Target := Target + ' ANALYZE'; + Result := Format('VACUUM %s %s',[Target, FTableName]); +end; + +end. diff --git a/PSQLTypes.pas b/Source/PSQLTypes.pas similarity index 97% rename from PSQLTypes.pas rename to Source/PSQLTypes.pas index 146abb9..eba45f2 100644 --- a/PSQLTypes.pas +++ b/Source/PSQLTypes.pas @@ -1,3575 +1,3575 @@ -{$I pSQLDAC.inc} -unit PSQLTypes; - -{SVN revision: $Id$} - -{$Z+,T-} //taken from MySQLDAC -interface - -uses {$IFDEF FPC}LCLIntf,{$ENDIF} - Classes, SysUtils, Math - - {$IFNDEF NEXTGEN} - {$IFDEF DELPHI_12}, AnsiStrings{$ENDIF} - {$ENDIF} - - {$IFDEF DELPHI_6}, FmtBcd{$ENDIF} - {$IFDEF DELPHI_12}, SqlTimSt, PSQLGeomTypes{$ENDIF} - {$IFDEF MSWINDOWS}, Windows{$ENDIF} - {$IFDEF ANDROID}, System.IOUtils {$ENDIF} - {$IFDEF MACOS}, Macapi.CoreServices{$ENDIF} - {$IFDEF NEXTGEN}, Generics.Collections{$ENDIF}; -{$IFDEF DELPHI_12} - {$NOINCLUDE PSQLGeomTypes} -{$ENDIF} - - -//============================================================================// -// Result Error Field Codes // -//============================================================================// -const - PG_DIAG_SEVERITY = ord('S'); - PG_DIAG_SQLSTATE = ord('C'); - PG_DIAG_MESSAGE_PRIMARY = ord('M'); - PG_DIAG_MESSAGE_DETAIL = ord('D'); - PG_DIAG_MESSAGE_HINT = ord('H'); - PG_DIAG_STATEMENT_POSITION = ord('P'); - PG_DIAG_INTERNAL_POSITION = ord('p'); - PG_DIAG_INTERNAL_QUERY = ord('q'); - PG_DIAG_CONTEXT = ord('W'); - PG_DIAG_SCHEMA_NAME = ord('s'); - PG_DIAG_TABLE_NAME = ord('t'); - PG_DIAG_COLUMN_NAME = ord('c'); - PG_DIAG_DATATYPE_NAME = ord('d'); - PG_DIAG_CONSTRAINT_NAME = ord('n'); - PG_DIAG_SOURCE_FILE = ord('F'); - PG_DIAG_SOURCE_LINE = ord('L'); - PG_DIAG_SOURCE_FUNCTION = ord('R'); - - -//============================================================================// -// Option flags for PQcopyResult // -//============================================================================// -const - PG_COPYRES_ATTRS = 01; - PG_COPYRES_TUPLES = 02; // Implies PG_COPYRES_ATTRS - PG_COPYRES_EVENTS = 04; - PG_COPYRES_NOTICEHOOKS = 08; - -//============================================================================// -// Error Categories // -//============================================================================// -const - ERRBASE_NONE = 0; { No error } - ERRBASE_NOTFOUND = $2200; { Object of interest Not Found } - ERRBASE_INVALIDREQ = $2700; { Invalid Request } - ERRBASE_SEC = $2900; { Access Violation - Security related } - ERRBASE_IC = $2A00; { Invalid context } - ERRBASE_QUERY = $2E00; { Query related } - ERRBASE_CAPABILITY = $3000; { Capability not supported } - ERRBASE_OTHER = $3300; { Miscellaneous } -//=============================================================================// -// Error Codes By Category // -//=============================================================================// - ERRCODE_NONE = 0; - DBIERR_NONE = (ERRBASE_NONE + ERRCODE_NONE); - ERRCODE_BOF = 1; { Beginning of Virtual table } - ERRCODE_EOF = 2; { End of Virtual table } - ERRCODE_NOCURRREC = 5; { No current record } - ERRCODE_RECNOTFOUND = 6; { Record was not found } - ERRCODE_ENDOFBLOB = 7; { End of Blob reached } - DBIERR_BOF = (ERRBASE_NOTFOUND + ERRCODE_BOF); - DBIERR_EOF = (ERRBASE_NOTFOUND + ERRCODE_EOF); - DBIERR_NOCURRREC = (ERRBASE_NOTFOUND + ERRCODE_NOCURRREC); - DBIERR_RECNOTFOUND = (ERRBASE_NOTFOUND + ERRCODE_RECNOTFOUND); - DBIERR_ENDOFBLOB = (ERRBASE_NOTFOUND + ERRCODE_ENDOFBLOB); - ERRCODE_INVALIDPARAM = 2; { Generic invalid parameter } - ERRCODE_INVALIDHNDL = 6; { Invalid handle to the function } - ERRCODE_NOSUCHINDEX = 13; { 0x0d Index does not exist } - ERRCODE_INVALIDBLOBOFFSET = 14; { 0x0e Invalid Offset into the Blob } - ERRCODE_INVALIDRECSTRUCT = 19; { 0x13 Invalid record structure } - ERRCODE_NOSUCHTABLE = 40; { 0x28 No such table } - ERRCODE_NOSUCHFILTER = 66; { 0x42 Filter handle is invalid } - DBIERR_INVALIDPARAM = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDPARAM); - DBIERR_INVALIDHNDL = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDHNDL); - DBIERR_NOSUCHINDEX = (ERRBASE_INVALIDREQ + ERRCODE_NOSUCHINDEX); - DBIERR_INVALIDBLOBOFFSET = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDBLOBOFFSET); - DBIERR_INVALIDRECSTRUCT = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDRECSTRUCT); - DBIERR_NOSUCHTABLE = (ERRBASE_INVALIDREQ + ERRCODE_NOSUCHTABLE); - DBIERR_NOSUCHFILTER = (ERRBASE_INVALIDREQ + ERRCODE_NOSUCHFILTER); -{ ERRCAT_SECURITY } -{ =============== } - ERRCODE_NOTSUFFTABLERIGHTS = 2; { Not sufficient table rights for operation } - DBIERR_NOTSUFFTABLERIGHTS = (ERRBASE_SEC + ERRCODE_NOTSUFFTABLERIGHTS); -{ ERRCAT_INVALIDCONTEXT } -{ ===================== } - ERRCODE_NOTABLOB = 1; { Field is not a blob } - ERRCODE_TABLEREADONLY = 11; { 0x0b Table is read only } - ERRCODE_NOASSOCINDEX = 12; { 0x0c No index associated with the cursor } - DBIERR_NOTABLOB = (ERRBASE_IC + ERRCODE_NOTABLOB); - DBIERR_TABLEREADONLY = (ERRBASE_IC + ERRCODE_TABLEREADONLY); - DBIERR_NOASSOCINDEX = (ERRBASE_IC + ERRCODE_NOASSOCINDEX); -{ ERRCAT_NETWORK } -{ ERRCAT_QUERY } -{ ============ } - DBICODE_QRYEMPTY = 110; { 0x6e } - DBIERR_QRYEMPTY = (ERRBASE_QUERY+ DBICODE_QRYEMPTY); -{ END_OF_QUERY_MESSAGES } - -{ ERRCAT_CAPABILITY } -{ ================= } - ERRCODE_NOTSUPPORTED = 1; { Capability not supported } - DBIERR_NOTSUPPORTED = (ERRBASE_CAPABILITY + ERRCODE_NOTSUPPORTED); -{ ERRCAT_OTHER } -{ ============ } - ERRCODE_UPDATEABORT = 6; { Update operation aborted } - DBIERR_UPDATEABORT = (ERRBASE_OTHER + ERRCODE_UPDATEABORT); - -///////////////////////////////////////////////////////////////////////////// -// COMPATIBILITY TYPES // -///////////////////////////////////////////////////////////////////////////// -type - PAnsiDACChar = {$IFDEF NEXTGEN}MarshaledAString{$ELSE}PAnsiChar{$ENDIF}; - PAnsiDACBytesChar = {$IFDEF NEXTGEN}PByte{$ELSE}PAnsiChar{$ENDIF}; - AnsiDACChar = {$IFDEF NEXTGEN}Char{$ELSE}AnsiChar{$ENDIF}; - DACAString = {$IFDEF NEXTGEN}String{$ELSE}AnsiString{$ENDIF}; - DACABytesString = {$IFDEF NEXTGEN}TBytes{$ELSE}AnsiString{$ENDIF}; - AnsiDACByteChar = {$IFDEF NEXTGEN}Byte{$ELSE}AnsiChar{$ENDIF}; -{$IFDEF DELPHI_16} - DACPointerInt = NativeInt; -{$ELSE} - DACPointerInt = integer; -{$ENDIF} - -const - START_STR_INDEX = {$IFDEF ZEROBASEDSTRINGS}0{$ELSE}1{$ENDIF}; - -{$IFNDEF DELPHI_12} - type - NativeUInt = cardinal; -{$ENDIF} - -{$IFDEF UNDER_DELPHI_6} - type - PBoolean = ^Boolean; - PWordBool = ^WordBool; - - TFormatSettings = record - CurrencyFormat: Byte; - NegCurrFormat: Byte; - ThousandSeparator: Char; - DecimalSeparator: Char; - CurrencyDecimals: Byte; - DateSeparator: Char; - TimeSeparator: Char; - ListSeparator: Char; - CurrencyString: string; - ShortDateFormat: string; - LongDateFormat: string; - TimeAMString: string; - TimePMString: string; - ShortTimeFormat: string; - LongTimeFormat: string; - ShortMonthNames: array[1..12] of string; - LongMonthNames: array[1..12] of string; - ShortDayNames: array[1..7] of string; - LongDayNames: array[1..7] of string; - TwoDigitYearCenturyWindow: Word; - end; -{$ENDIF} - -const - NAMEDATALEN = 64; - DATELEN = length('2001-02-17'); - TIMEZONELEN = length('+10:30'); - TIMESTAMPTZLEN = length('2001-02-17 07:08:40.123456+10:30'); - TIMETZLEN = length('13:45:35.4880123457+13:40'); - UUIDLEN = length('{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}'); - INETLEN = length('7628:0d18:11a3:09d7:1f34:8a2e:07a0:765d/128'); - MACADDRLEN = length('08:00:2b:01:02:03'); - OIDNAMELEN = 36; - INV_WRITE = $00020000; - INV_READ = $00040000; - PG_SEEK_SET = 0; // Seek from beginning of file - PG_SEEK_CUR = 1; // Seek from current position - PG_SEEK_END = 2; // Seek from end of file - DELIMITERS : string = ' .:;,+-<>/*%^=()[]|&~@#$\`{}!?'#10#13; - PSQL_PORT = 5432; - MINLONGINT = -MaxLongInt; - MAX_BLOB_SIZE = 8192; //Max Blob size for read and write operation - MAX_CHAR_LEN = 8192; //Max character length allowed in TField descendants - MAX_ENCODING_ID = 42; //Max encoding id for pg_encoding_to_char - InvalidOid : cardinal = 0; - NUMERIC_PREC = 32; //Default precision for TFmtBcdField if not specified for NUMERIC - NUMERIC_SCALE = 6; //Default scale for TFmtBcdField if not specified for NUMERIC - - -const //date/time convertion - TIMESTAMP_MODE = 0; - DATE_MODE = 1; - TIME_MODE = 2; - -{$IFDEF FPC} - const - HINSTANCE_ERROR = 32; -{$ELSE} - {$IFNDEF MSWINDOWS} - const - HINSTANCE_ERROR = 32; - {$ENDIF} -{$ENDIF} - -const - LIBEAY_DLL : string = 'libcrypto-1_1.dll'; - SSLEAY_DLL : string = 'libssl-1_1.dll'; - -var - PSQL_DLL : string = - {$IFDEF MSWINDOWS}'libpq.dll'{$ENDIF} - {$IFDEF MACOS}'libpq.dylib'{$ENDIF} - {$IFDEF ANDROID}'libpq.so'{$ENDIF} - {$IFDEF LINUX}'libpq.so'{$ENDIF}; - PSQL_DLL_WITHOUT_SLL : string = 'libpq-without-SSL.dll'; - - - SQLLibraryHandle : THandle = HINSTANCE_ERROR; - OEMConv : Boolean; //Global OEM->ANSI Variable - PSQL_FS : TFormatSettings; - -type - - - TPSQLDACAbout = class - end; - - TPSQLDatasetSortCompare = function(Dataset: TObject; Value1, Value2: Variant; - FieldIndex: integer): Integer; - -////////////////////////////////////////////////////////////////// -// FIELD TYPES // -////////////////////////////////////////////////////////////////// -const - FIELD_TYPE_BOOL = 16; - FIELD_TYPE_BYTEA = 17; - FIELD_TYPE_CHAR = 18; - FIELD_TYPE_NAME = 19; - FIELD_TYPE_INT8 = 20; - FIELD_TYPE_INT2 = 21; - FIELD_TYPE_INT2VECTOR = 22; - FIELD_TYPE_INT4 = 23; - FIELD_TYPE_REGPROC = 24; - FIELD_TYPE_TEXT = 25; - FIELD_TYPE_OID = 26; - FIELD_TYPE_TID = 27; - FIELD_TYPE_XID = 28; - FIELD_TYPE_CID = 29; - FIELD_TYPE_OIDVECTOR = 30; - FIELD_TYPE_SET = 32; - FIELD_TYPE_SMGR = 210; - FIELD_TYPE_POINT = 600; - FIELD_TYPE_LSEG = 601; - FIELD_TYPE_PATH = 602; - FIELD_TYPE_BOX = 603; - FIELD_TYPE_POLYGON = 604; - FIELD_TYPE_LINE = 628; - FIELD_TYPE_A_LINE = 629; - FIELD_TYPE_CIDR = 650; - FIELD_TYPE_A_CIDR = 651; - FIELD_TYPE_FLOAT4 = 700; - FIELD_TYPE_FLOAT8 = 701; - FIELD_TYPE_ABSTIME = 702; - FIELD_TYPE_RELTIME = 703; - FIELD_TYPE_TINTERVAL = 704; - FIELD_TYPE_UNKNOWN = 705; - FIELD_TYPE_CIRCLE = 718; - FIELD_TYPE_A_CIRCLE = 719; - FIELD_TYPE_MONEY = 790; - FIELD_TYPE_A_MONEY = 791; - FIELD_TYPE_MACADDR = 829; - FIELD_TYPE_MACADDR8 = 774; - FIELD_TYPE_INET = 869; - FIELD_TYPE_A_BOOL = 1000; - FIELD_TYPE_A_BYTEA = 1001; - FIELD_TYPE_A_CHAR = 1002; - FIELD_TYPE_A_NAME = 1003; - FIELD_TYPE_A_INT2 = 1005; - FIELD_TYPE_A_INT28 = 1006; - FIELD_TYPE_A_INT4 = 1007; - FIELD_TYPE_A_REGPROC = 1008; - FIELD_TYPE_A_TEXT = 1009; - FIELD_TYPE_A_TID = 1010; - FIELD_TYPE_A_XID = 1011; - FIELD_TYPE_A_CID = 1012; - FIELD_TYPE_A_OID8 = 1013; - FIELD_TYPE_A_BPCHAR = 1014; - FIELD_TYPE_A_VARCHAR = 1015; - FIELD_TYPE_A_POINT = 1017; - FIELD_TYPE_A_LSEG = 1018; - FIELD_TYPE_A_PATH = 1019; - FIELD_TYPE_A_BOX = 1020; - FIELD_TYPE_A_FLOAT4 = 1021; - FIELD_TYPE_A_FLOAT8 = 1022; - FIELD_TYPE_A_ABSTIME = 1023; - FIELD_TYPE_A_RELTIME = 1024; - FIELD_TYPE_A_TINTERVAL = 1025; - FIELD_TYPE_A_FILENAME = 1026; - FIELD_TYPE_A_POLYGON = 1027; - FIELD_TYPE_A_OID = 1028; - FIELD_TYPE_ACLITEM = 1033; - FIELD_TYPE_A_ACLITEM = 1034; - FIELD_TYPE_A_MACADDR = 1040; - FIELD_TYPE_A_MACADDR8 = 775; - FIELD_TYPE_A_INET = 1041; - FIELD_TYPE_BPCHAR = 1042; - FIELD_TYPE_VARCHAR = 1043; - FIELD_TYPE_DATE = 1082; - FIELD_TYPE_TIME = 1083; - FIELD_TYPE_A_TIMESTAMP = 1115; - FIELD_TYPE_TIMESTAMP = 1114; - FIELD_TYPE_A_DATE = 1182; - FIELD_TYPE_A_TIME = 1183; - FIELD_TYPE_A_TIMESTAMPTZ = 1185; - FIELD_TYPE_TIMESTAMPTZ = 1184; - FIELD_TYPE_A_DATETIME = 1185; - FIELD_TYPE_INTERVAL = 1186; - FIELD_TYPE_A_INTERVAL = 1187; - FIELD_TYPE_A_TIMETZ = 1270; - FIELD_TYPE_TIMETZ = 1266; - FIELD_TYPE_A_BIT = 1561; - FIELD_TYPE_BIT = 1560; - FIELD_TYPE_A_VARBIT = 1563; - FIELD_TYPE_VARBIT = 1562; - FIELD_TYPE_A_NUMERIC = 1231; - FIELD_TYPE_NUMERIC = 1700; - FIELD_TYPE_UUID = 2950; - FIELD_TYPE_JSON = 114; - FIELD_TYPE_XML = 142; - FIELD_TYPE_TSVECTOR = 3614; - FIELD_TYPE_GTSVECTOR = 3642; - FIELD_TYPE_TSQUERY = 3615; - FIELD_TYPE_REGCONFIG = 3734; - FIELD_TYPE_REGDICTIONARY = 3769; - FIELD_TYPE_JSONB = 3802; - - //range types - FIELD_TYPE_INT4RANGE = 3904; - FIELD_TYPE_NUMRANGE = 3906; - FIELD_TYPE_TSRANGE = 3908; - FIELD_TYPE_TSTZRANGE = 3910; - FIELD_TYPE_DATERANGE = 3912; - FIELD_TYPE_INT8RANGE = 3926; - FIELD_TYPE_A_INT8RANGE = 3927; - - - PSEUDO_TYPE_VOID = 2278; - PSEUDO_TYPE_TRIGGER = 2279; - PSEUDO_TYPE_LANGHANDLER = 2280; - PSEUDO_TYPE_RECORD = 2249; - PSEUDO_TYPE_CSTRING = 2275; - PSEUDO_TYPE_A_CSTRING = 1263; - PSEUDO_TYPE_INTERNAL = 2281; - PSEUDO_TYPE_ANYENUM = 3500; - PSEUDO_ANY_ARRAY = 2277; - PSEUDO_ANY_NONARRAY = 2276; - PSEUDO_ANY_ELEMENT = 2283; - PSEUDO_OPAQUE = 2282; - PSEUDO_ANY_ENUM = 3500; - - MAX_BUILTIN_TYPE_OID = FIELD_TYPE_A_INT8RANGE; //pg: 04.04.2012 need to be changed if new built-in type appears - - - MAXARRFLDTYPES = 38; - - FldArrayType: array[0..MAXARRFLDTYPES-1] of Cardinal = ( - FIELD_TYPE_A_LINE, FIELD_TYPE_A_CIDR, FIELD_TYPE_A_CIRCLE, FIELD_TYPE_A_MONEY, FIELD_TYPE_A_BOOL, FIELD_TYPE_A_BYTEA, - FIELD_TYPE_A_CHAR, FIELD_TYPE_A_NAME, FIELD_TYPE_A_INT2, FIELD_TYPE_A_INT28, FIELD_TYPE_A_INT4, FIELD_TYPE_A_REGPROC, - FIELD_TYPE_A_TEXT, FIELD_TYPE_A_TID, FIELD_TYPE_A_XID, FIELD_TYPE_A_CID, FIELD_TYPE_A_OID8, FIELD_TYPE_A_BPCHAR, - FIELD_TYPE_A_VARCHAR, FIELD_TYPE_A_POINT, FIELD_TYPE_A_LSEG, FIELD_TYPE_A_PATH, FIELD_TYPE_A_BOX, FIELD_TYPE_A_FLOAT4, - FIELD_TYPE_A_FLOAT8, FIELD_TYPE_A_ABSTIME,FIELD_TYPE_A_RELTIME,FIELD_TYPE_A_TINTERVAL,FIELD_TYPE_A_FILENAME,FIELD_TYPE_A_POLYGON, - FIELD_TYPE_A_OID, FIELD_TYPE_A_ACLITEM,FIELD_TYPE_A_MACADDR,FIELD_TYPE_A_INET, FIELD_TYPE_A_DATE, FIELD_TYPE_A_TIME, - FIELD_TYPE_A_DATETIME,FIELD_TYPE_A_INTERVAL); - -////////////////////////////////////////////////////////////////// -// Collation Constants // -////////////////////////////////////////////////////////////////// -const - DEFAULT_COLLATION_OID = 100; - C_COLLATION_OID = 950; - POSIX_COLLATION_OID = 951; - -////////////////////////////////////////////////////////////////// -// Plain API Types definition // -////////////////////////////////////////////////////////////////// -type - - //used to determine what native type used to store BLOBs - TNativeBLOBType = (nbtNotBLOB, nbtBytea, nbtOID); - - //used to determine what native presentation used for Bytea - TNativeByteaFormat = (nbfEscape, nbfHex); - - - MemPtr = ^MemArray; - MemArray = array[0..MaxInt-1] of Byte; - - Oid = Cardinal; - POid = ^Oid; - TDynOidArray = array of Oid; - - ConnStatusType = ( - CONNECTION_OK, - CONNECTION_BAD, - //Non-blocking mode only below here - CONNECTION_STARTED, // Waiting for connection to be made - CONNECTION_MADE, // Connection OK; waiting to send - CONNECTION_AWAITING_RESPONSE, // Waiting for a response from the postmaster - CONNECTION_AUTH_OK, // Received authentication; waiting for backend startup - CONNECTION_SETENV, // Negotiating environment - CONNECTION_SSL_STARTUP, // Negotiating SSL - CONNECTION_NEEDED // Internal state: connect() needed - ); - - PollingStatusType = ( - PGRES_POLLING_FAILED, - PGRES_POLLING_READING, // These two indicate that one may - PGRES_POLLING_WRITING, // use select before polling again - PGRES_POLLING_OK, - PGRES_POLLING_ACTIVE // unused; keep for awhile for backwards compatibility - ); - - ExecStatusType = ( - PGRES_EMPTY_QUERY, - PGRES_COMMAND_OK, // a query command that doesn't return anything was executed properly by the backend - PGRES_TUPLES_OK, // a query command that returns tuples was executed properly by the backend, PGresult contains the result tuples - PGRES_COPY_OUT, // Copy Out data transfer in progress - PGRES_COPY_IN, // Copy In data transfer in progress - PGRES_BAD_RESPONSE, // an unexpected response was recv'd from the backend - PGRES_NONFATAL_ERROR, // notice or warning message - PGRES_FATAL_ERROR, //query failed - PGRES_COPY_BOTH, // Copy In/Out data transfer in progress - PGRES_SINGLE_TUPLE // single tuple from larger resultset - ); - -// String descriptions of the ExecStatusTypes - pgresStatus = array[$00..$ff] of PAnsiDACChar; - - TErrorVerbosity = (evTERSE, evDEFAULT, evVERBOSE); - - TTransactionStatusType = ( - trstIDLE, // connection idle - trstACTIVE, // command in progress - trstINTRANS, // idle, within transaction block - trstINERROR, // idle, within failed transaction - trstUNKNOWN); // cannot determine status - - TPingStatus = ( - pstOK, //The server is running and appears to be accepting connections - pstReject, //The server is running but is in a state that disallows connections (startup, shutdown, or crash recovery) - pstNoResponse, //The server could not be contacted - pstNoAttempt); //No attempt was made to contact the server due to incorrect parameters - -//////////////////////////////////////////////////////////////////// -// PGconn encapsulates a connection to the backend. // -// The contents of this struct are not supposed to be known to // -// applications. // -//////////////////////////////////////////////////////////////////// - PGconn = Pointer; - PPGconn = Pointer; - -// PGresult encapsulates the result of a query (or more precisely, of a single -// SQL command --- a query string given to PQsendQuery can contain multiple -// commands and thus return multiple PGresult objects). -// The contents of this struct are not supposed to be known to applications. - PGresult = Pointer; - PPGresult = ^PGresult; - -// PGnotify represents the occurrence of a NOTIFY message. -// Ideally this would be an opaque typedef, but it's so simple that it's -// unlikely to change. -// NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's, -// whereas in earlier versions it was always your own backend's PID. - PPGnotify = ^PGnotify; - PGnotify = record - relname: PAnsiDACChar; // name of relation containing data - be_pid: Integer; // process id of backend - extra: PAnsiDACChar; // extra notification - next: PPGnotify; // application should never use this - end; - - -// PQnoticeProcessor is the function type for the notice-message callback. - PQnoticeProcessor = procedure(arg: Pointer; message: PAnsiDACChar);cdecl; - -// Print options for PQprint() -// We can't use the conventional "bool", because we are designed to be -// included in a user's program, and user may already have that type -// defined. Pqbool, on the other hand, is unlikely to be used. - - PPAnsiDACChar = ^PAnsiDACChar; - - PQprintOpt = packed record - header: Byte; { print output field headings and row count } - align: Byte; { fill align the fields } - standard: Byte; { old brain dead format } - html3: Byte; { output html tables } - expanded: Byte; { expand tables } - pager: Byte; { use pager for output if needed } - fieldSep: PAnsiDACChar; { field separator } - tableOpt: PAnsiDACChar; { insert to HTML } - caption: PAnsiDACChar; { HTML
} - fieldName: PPAnsiDACChar; { null terminated array of repalcement field names } - end; - - PPQprintOpt = ^PQprintOpt; - -////////////////////////////////////////////////////////////////////////////////// -// Structure for the conninfo parameter definitions returned by PQconndefaults // -////////////////////////////////////////////////////////////////////////////////// - PQconninfoOption = packed record - keyword: PAnsiDACChar; { The keyword of the option } - envvar: PAnsiDACChar; { Fallback environment variable name } - compiled: PAnsiDACChar; { Fallback compiled in default value } - val: PAnsiDACChar; { Options value } - lab: PAnsiDACChar; { Label for field in connect dialog } - dispchar: PAnsiDACChar; { Character to display for this field - in a connect dialog. Values are: - "" Display entered value as is - "*" Password field - hide value - "D" Debug options - don't - create a field by default } - dispsize: Integer; { Field size in characters for dialog } - end; - - PPQConninfoOption = ^PQconninfoOption; - -////////////////////////////////////////////////////////////////// -// Plain API Function types definition // -////////////////////////////////////////////////////////////////// - TPQlibVersion = function(): Integer; cdecl; - TPQisthreadsafe = function(): Integer; cdecl; - TPQconnectdb = function(ConnInfo: PAnsiDACChar): PPGconn; cdecl; //blocking manner - TPQconnectStart = function(ConnInfo: PAnsiDACChar): PPGconn; cdecl; //non-blocking manner - TPQconnectdbParams = function(Keywords: PPAnsiDACChar; Values: PPAnsiDACChar; ExpandDBName: integer): PPGconn; cdecl; //blocking manner - TPQping = function(ConnInfo: PAnsiDACChar): TPingStatus; cdecl; - TPQpingParams = function(Keywords: PPAnsiDACChar; Values: PPAnsiDACChar; ExpandDBName: integer): TPingStatus; - TPQconnectPoll = function (Handle : PPGconn): PollingStatusType; cdecl; - TPQsetdbLogin = function(Host, Port, Options, Tty, Db, User, Passwd: PAnsiDACChar): PPGconn; cdecl; - TPQconndefaults = function: PPQconninfoOption; cdecl; - TPQfinish = procedure(Handle: PPGconn); cdecl; - TPQreset = procedure(Handle: PPGconn); cdecl; - TPQrequestCancel = function(Handle: PPGconn): Integer; cdecl; - TPQdb = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQuser = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQpass = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQhost = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQport = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQtty = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQoptions = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQstatus = function(Handle: PPGconn): ConnStatusType; cdecl; - TPQerrorMessage = function(Handle: PPGconn): PAnsiDACChar; cdecl; - TPQsocket = function(Handle: PPGconn): Integer; cdecl; - TPQbackendPID = function(Handle: PPGconn): Integer; cdecl; - TPQparameterStatus = function(Handle: PPGconn; paramName: PAnsiDACChar): PAnsiDACChar; cdecl; - TPQserverVersion = function(Handle: PPGconn): Integer; cdecl; - TPQtransactionStatus = function(Handle: PPGconn): TTransactionStatusType; cdecl; - TPQgetssl = function(Handle: PPGconn): pointer; cdecl; //point to SSL structure, see OpenSSL manual for details - TPQtrace = procedure(Handle: PPGconn; DebugPort: Pointer); cdecl; - TPQuntrace = procedure(Handle: PPGconn); cdecl; - TPQsetNoticeProcessor = function(Handle: PPGconn; Proc: PQnoticeProcessor; Arg: Pointer): Pointer; cdecl; - TPQprepare = function(Handle: PPGconn; - StmtName: PAnsiDACChar; - Query: PAnsiDACChar; - nParams: integer; - paramTypes: POid): PPGresult; cdecl; - TPQexecPrepared = function(Handle: PPGconn; - StmtName: PAnsiDACChar; - nParams: integer; - paramValues: PPAnsiDACChar; - paramLengths: PInteger; - paramFormats: PInteger; - resultFormat: integer): PPGresult; cdecl; - - TPQexec = function(Handle: PPGconn; Query: PAnsiDACChar): PPGresult; cdecl; - - TPQexecParams = function(Handle: PPGconn; - Query: PAnsiDACChar; - nParams: integer; - paramTypes: POid; - paramValues: PPAnsiDACChar; - paramLengths: PInteger; - paramFormats: PInteger; - resultFormat: integer): PPGresult; cdecl; - TPQresultErrorField = function(Result: PPGresult; fieldcode: integer): PAnsiDACChar; cdecl; - TPQnotifies = function(Handle: PPGconn): PPGnotify; cdecl; - TPQsetSingleRowMode = function(Handle: PPGconn): integer; cdecl; - TPQsendQuery = function(Handle: PPGconn; Query: PAnsiDACChar): Integer; cdecl; - TPQgetResult = function(Handle: PPGconn): PPGresult; cdecl; - TPQisBusy = function(Handle: PPGconn): Integer; cdecl; - TPQconsumeInput = function(Handle: PPGconn): Integer; cdecl; - TPQgetline = function(Handle: PPGconn; - Str: PAnsiDACChar; - length: Integer): Integer; cdecl; - TPQputline = function(Handle: PPGconn; - Str: PAnsiDACChar): Integer; cdecl; - TPQgetlineAsync = function(Handle: PPGconn; - Buffer: PAnsiDACChar; - BufSize: Integer): Integer; cdecl; - TPQputnbytes = function(Handle: PPGconn; - Buffer: PAnsiDACChar; - NBytes: Integer): Integer; cdecl; - TPQendcopy = function(Handle: PPGconn): Integer; cdecl; - TPQgetCopyData = function(Handle: PPGConn; - Buffer: PPAnsiDACChar; - Async: integer = 0): Integer; cdecl; - TPQputCopyData = function(Handle: PPGConn; - Buffer: PAnsiDACChar; - Len: integer): Integer; cdecl; - TPQputCopyEnd = function(Handle: PPGConn; - Buffer: PAnsiDACChar = nil): Integer; cdecl; - TPQresultStatus = function(Result: PPGresult): ExecStatusType; cdecl; - TPQresultErrorMessage = function(Result: PPGresult): PAnsiDACChar; cdecl; - TPQntuples = function(Result: PPGresult): Integer; cdecl; - TPQnfields = function(Result: PPGresult): Integer; cdecl; - TPQbinaryTuples = function(Result: PPGresult): Integer; cdecl; - TPQfname = function(Result: PPGresult; field_num: Integer): PAnsiDACChar; cdecl; - TPQfnumber = function(Result: PPGresult; field_name: PAnsiDACChar): Integer; cdecl; - TPQftype = function(Result: PPGresult; field_num: Integer): Oid; cdecl; - TPQfformat = function(Result: PPGresult; field_num: Integer): Oid; cdecl; - TPQftable = function(Result: PPGresult; field_num: Integer): Oid; cdecl; - TPQftablecol = function(Result: PPGresult; field_num: Integer): Integer; cdecl; - TPQfsize = function(Result: PPGresult; field_num: Integer): Integer; cdecl; - TPQfmod = function(Result: PPGresult; field_num: Integer): Integer; cdecl; - TPQcmdStatus = function(Result: PPGresult): PAnsiDACChar; cdecl; - TPQoidValue = function(Result: PPGresult): Oid; cdecl; - TPQoidStatus = function(Result: PPGresult): PAnsiDACChar; cdecl; - TPQcmdTuples = function(Result: PPGresult): PAnsiDACChar; cdecl; - TPQgetvalue = function(Result: PPGresult; - tup_num: Integer; - field_num: Integer): PAnsiDACChar; cdecl; - TPQsetvalue = function(Result: PPGresult; - tup_num: Integer; - field_num: Integer; - value: PAnsiDACChar; - len: integer): integer; cdecl; - TPQcopyResult = function(Result: PPGresult; flags: integer): PPGresult; cdecl; - TPQgetlength = function(Result: PPGresult; - tup_num: Integer; - field_num: Integer): Integer; cdecl; - TPQgetisnull = function(Result: PPGresult; - tup_num: Integer; - field_num: Integer): Integer; cdecl; - TPQclear = procedure(Result: PPGresult); cdecl; - TPQmakeEmptyPGresult = function(Handle: PPGconn; - status: ExecStatusType): PPGresult; cdecl; - TPQEscapeByteaConn = function(Handle: PPGconn; - from: PAnsiDACChar; - from_length: integer; - var to_length: integer): PAnsiDACChar; cdecl; - TPQUnEscapeBytea = function(from: PAnsiDACChar; var to_length: integer): PAnsiDACChar; cdecl; - TPQEscapeStringConn = function(Handle: PPGconn; - to_str: PAnsiDACChar; - const from_str: PAnsiDACChar; - from_size: cardinal; - var Error: integer): cardinal; cdecl; - TPQFreeMem = procedure(Ptr: Pointer); cdecl; - TPQsetClientEncoding = function(Handle: PPGconn; encoding: PAnsiDACChar): integer; cdecl; - TPQsetErrorVerbosity = function(Handle: PPGconn; verbosity: TErrorVerbosity): TErrorVerbosity; cdecl; - TPQclientEncoding = function(Handle: PPGconn): integer; cdecl; - Tpg_encoding_to_char = function(encoding_id: integer): PAnsiDACChar; cdecl; - Tlo_open = function(Handle: PPGconn; - lobjId: Oid; - mode: Integer): Integer; cdecl; - Tlo_close = function(Handle: PPGconn; fd: Integer): Integer; cdecl; - Tlo_read = function(Handle: PPGconn; - fd: Integer; - buf: PAnsiDACChar; - len: Integer): Integer; cdecl; - Tlo_write = function(Handle: PPGconn; - fd: Integer; - buf: PAnsiDACChar; - len: Integer): Integer; cdecl; - Tlo_lseek = function(Handle: PPGconn; - fd: Integer; - offset: Integer; - whence: Integer): Integer; cdecl; - Tlo_creat = function(Handle: PPGconn; mode: Integer): Oid; cdecl; - Tlo_tell = function(Handle: PPGconn; fd: Integer): Integer; cdecl; - Tlo_unlink = function(Handle: PPGconn; lobjId: Oid): Integer; cdecl; - Tlo_import = function(Handle: PPGconn; filename: PAnsiDACChar): Oid; cdecl; - Tlo_export = function(Handle: PPGconn; lobjId: Oid; filename: PAnsiDACChar): Integer; cdecl; - - -////////////////////////////////////////////////////////////////// -// Plain API function variables definition // -////////////////////////////////////////////////////////////////// - -var - PQlibVersion: TPQlibVersion; - PQisthreadsafe: TPQisthreadsafe; - PQconnectdb: TPQconnectdb; - PQconnectdbParams: TPQconnectdbParams; - PQconnectStart: TPQconnectStart; - PQping: TPQping; - PQpingParams: TPQpingParams; - PQconnectPoll: TPQconnectPoll; - PQsetdbLogin: TPQsetdbLogin; - PQconndefaults: TPQconndefaults; - PQfinish: TPQfinish; - PQreset: TPQreset; - PQrequestCancel: TPQrequestCancel; - PQdb: TPQdb; - PQuser: TPQuser; - PQpass: TPQpass; - PQhost: TPQhost; - PQport: TPQport; - PQtty: TPQtty; - PQoptions: TPQoptions; - PQstatus: TPQstatus; - PQerrorMessage: TPQerrorMessage; - PQsocket: TPQsocket; - PQparameterStatus: TPQparameterStatus; - PQserverVersion: TPQserverVersion; - PQbackendPID: TPQbackendPID; - PQtransactionStatus: TPQtransactionStatus; - PQgetssl: TPQgetssl; - PQtrace: TPQtrace; - PQuntrace: TPQuntrace; - PQsetNoticeProcessor: TPQsetNoticeProcessor; - PQprepare: TPQprepare; - PQexecPrepared: TPQexecPrepared; - PQexec: TPQexec; - PQsendQuery: TPQsendQuery; - PQexecParams: TPQexecParams; - PQresultErrorField:TPQresultErrorField; - PQnotifies: TPQnotifies; - PQsetSingleRowMode: TPQsetSingleRowMode; - PQgetResult: TPQgetResult; - PQisBusy: TPQisBusy; - PQconsumeInput: TPQconsumeInput; - PQgetline: TPQgetline; - PQputline: TPQputline; - PQgetlineAsync: TPQgetlineAsync; - PQputnbytes: TPQputnbytes; - PQendcopy: TPQendcopy; - PQgetCopyData: TPQgetCopyData; - PQputCopyData: TPQputCopyData; - PQputCopyEnd: TPQputCopyEnd; - PQresultStatus: TPQresultStatus; - PQresultErrorMessage: TPQresultErrorMessage; - PQntuples: TPQntuples; - PQnfields: TPQnfields; - PQbinaryTuples: TPQbinaryTuples; - PQfname: TPQfname; - PQfnumber: TPQfnumber; - PQftype: TPQftype; - PQfformat: TPQfformat; - PQftable: TPQftable; - PQftablecol: TPQftablecol; - PQfsize: TPQfsize; - PQfmod: TPQfmod; - PQcmdStatus: TPQcmdStatus; - PQoidValue: TPQoidValue; - PQoidStatus: TPQoidStatus; - PQcmdTuples: TPQcmdTuples; - PQgetvalue: TPQgetvalue; - PQsetvalue: TPQsetvalue; - PQcopyResult: TPQcopyResult; - PQgetlength: TPQgetlength; - PQgetisnull: TPQgetisnull; - PQclear: TPQclear; - PQmakeEmptyPGresult: TPQmakeEmptyPGresult; - PQEscapeByteaConn: TPQEscapeByteaConn; - PQUnEscapeBytea: TPQUnEscapeBytea; - PQEscapeStringConn: TPQEscapeStringConn; - PQFreeMem: TPQFreeMem; - PQsetClientEncoding: TPQsetClientEncoding; - PQsetErrorVerbosity: TPQsetErrorVerbosity; - PQclientEncoding: TPQclientEncoding; - pg_encoding_to_char: Tpg_encoding_to_char; - lo_open: Tlo_open; - lo_close: Tlo_close; - lo_read: Tlo_read; - lo_write: Tlo_write; - lo_lseek: Tlo_lseek; - lo_creat: Tlo_creat; - lo_tell: Tlo_tell; - lo_unlink: Tlo_unlink; - lo_import: Tlo_import; - lo_export: Tlo_export; - -///////////////////////////////////////////////////////////////////////////////// -// BDE TYPE // -///////////////////////////////////////////////////////////////////////////////// - -resourcestring - SAutoSessionExclusive = 'Cannot enable AutoSessionName property with more than one session on a form or data-module'; - SAutoSessionExists = 'Cannot add a session to the form or data-module while session ''%s'' has AutoSessionName enabled'; - SAutoSessionActive = 'Cannot modify SessionName while AutoSessionName is enabled'; - SDuplicateDatabaseName = 'Duplicate database name ''%s'''; - SDuplicateSessionName = 'Duplicate session name ''%s'''; - SInvalidSessionName = 'Invalid session name %s'; - SDatabaseNameMissing = 'Database name missing'; - SSessionNameMissing = 'Session name missing'; - SDatabaseOpen = 'Cannot perform this operation on an open database'; - SDatabaseClosed = 'Cannot perform this operation on a closed database'; - SDatabaseHandleSet = 'Database handle owned by a different session'; - SSessionActive = 'Cannot perform this operation on an active session'; - SHandleError = 'Error creating cursor handle'; - SInvalidFloatField = 'Cannot convert field ''%s'' to a floating point value'; - SInvalidIntegerField = 'Cannot convert field ''%s'' to an integer value'; - SInvalidRangeType = 'Cannot convert value. Unsupported range type passed'; - STableMismatch = 'Source and destination tables are incompatible'; - SFieldAssignError = 'Fields ''%s'' and ''%s'' are not assignment compatible'; - SFieldNotRangeType = 'Field ''%s'' is not range type'; - SNoReferenceTableName = 'ReferenceTableName not specified for field ''%s'''; - SCompositeIndexError = 'Cannot use array of Field values with Expression Indices'; - SInvalidBatchMove = 'Invalid batch move parameters'; - SEmptySQLStatement = 'No SQL statement available'; - SNoParameterValue = 'No value for parameter ''%s'''; - SNoParameterType = 'No parameter type for parameter ''%s'''; - SLoginError = 'Cannot connect to database ''%s'''; - SLoginPrompt = 'Cannot call login prompt to database ''%s'''; - SInitError = 'An error occurred while attempting to initialize the Borland Database Engine (error $%.4x)'; - SDatabaseEditor = 'Da&tabase Editor...'; - SExplore = 'E&xplore'; - SLinkDetail = '''%s'' cannot be opened'; - SLinkMasterSource = 'The MasterSource property of ''%s'' must be linked to a DataSource'; - SLinkMaster = 'Unable to open the MasterSource Table'; - SGQEVerb = 'S&QL Builder...'; - SBindVerb = 'Define &Parameters...'; - SIDAPILangID = '0009'; - SDisconnectDatabase = 'Database is currently connected. Disconnect and continue?'; - SBDEError = 'BDE error $%.4x'; - SLookupSourceError = 'Unable to use duplicate DataSource and LookupSource'; - SLookupTableError = 'LookupSource must be connected to TTable component'; - SLookupIndexError = '%s must be the lookup table''s active index'; - SParameterTypes = ';Input;Output;Input/Output;Result'; - SInvalidParamFieldType = 'Must have a valid field type selected'; - STruncationError = 'Parameter ''%s'' truncated on output'; - SDataTypes = ';String;SmallInt;Integer;Word;Boolean;Float;Currency;BCD;Date;Time;DateTime;;;;Blob;Memo;Graphic;;;;;Cursor;'; - SResultName = 'Result'; - SDBCaption = '%s%s%s Database'; - SParamEditor = '%s%s%s Parameters'; - SIndexFilesEditor = '%s%s%s Index Files'; - SNoIndexFiles = '(None)'; - SIndexDoesNotExist = 'Index does not exist. Index: %s'; - SNoTableName = 'Missing TableName property'; - SNoDataSetField = 'Missing DataSetField property'; - SBatchExecute = 'E&xecute'; - SNoCachedUpdates = 'Not in cached update mode'; - SInvalidAliasName = 'Invalid alias name %s'; - SNoFieldAccess = 'Cannot access field ''%s'' in a filter'; - SUpdateSQLEditor = '&UpdateSQL Editor...'; - SNoDataSet = 'No dataset association'; - SUntitled = 'Untitled Application'; - SUpdateWrongDB = 'Cannot update, %s is not owned by %s'; - SUpdateFailed = 'Update failed'; - SSQLGenSelect = 'Must select at least one key field and one update field'; - SSQLNotGenerated = 'Update SQL statements not generated, exit anyway?'; - SSQLDataSetOpen = 'Unable to determine field names for %s'; - SLocalTransDirty = 'The transaction isolation level must be dirty read for local databases'; - SMissingDataSet = 'Missing DataSet property'; - SNoProvider = 'No provider available'; - SNotAQuery = 'Dataset is not a query'; - - SInvalidFieldSize = 'Invalid field size'; - SInvalidFieldKind = 'Invalid FieldKind'; - SInvalidFieldRegistration = 'Invalid field registration'; - SUnknownFieldType = 'Field ''%s'' is of an unknown type'; - SFieldNameMissing = 'Field name missing'; - SDuplicateFieldName = 'Duplicate field name ''%s'''; - SFieldNotFound = 'Field ''%s'' not found'; - SFieldAccessError = 'Cannot access field ''%s'' as type %s'; - SFieldValueError = 'Invalid value for field ''%s'''; - SFieldRangeError = '%g is not a valid value for field ''%s''. The allowed range is %g to %g'; - SBcdFieldRangeError = '%s is not a valid value for field ''%s''. The allowed range is %s to %s'; - SInvalidIntegerValue = '''%s'' is not a valid integer value for field ''%s'''; - SInvalidBoolValue = '''%s'' is not a valid boolean value for field ''%s'''; - SInvalidFloatValue = '''%s'' is not a valid floating point value for field ''%s'''; - SFieldTypeMismatch = 'Type mismatch for field ''%s'', expecting: %s actual: %s'; - SFieldSizeMismatch = 'Size mismatch for field ''%s'', expecting: %d actual: %d'; - SInvalidVarByteArray = 'Invalid variant type or size for field ''%s'''; - SFieldOutOfRange = 'Value of field ''%s'' is out of range'; -// SBCDOverflow = '(Overflow)'; - SCantAdjustPrecision = 'Error adjusting BCD precision'; - SFieldRequired = 'Field ''%s'' must have a value'; - SDataSetMissing = 'Field ''%s'' has no dataset'; - SInvalidCalcType = 'Field ''%s'' cannot be a calculated or lookup field'; - SFieldReadOnly = 'Field ''%s'' cannot be modified'; - SFieldIndexError = 'Field index out of range'; - SNoFieldIndexes = 'No index currently active'; - SNotIndexField = 'Field ''%s'' is not indexed and cannot be modified'; - SIndexFieldMissing = 'Cannot access index field ''%s'''; - SDuplicateIndexName = 'Duplicate index name ''%s'''; - SNoIndexForFields = 'No index for fields ''%s'''; - SIndexNotFound = 'Index ''%s'' not found'; - SDBDuplicateName = 'Duplicate name ''%s'' in %s'; - SCircularDataLink = 'Circular datalinks are not allowed'; - SLookupInfoError = 'Lookup information for field ''%s'' is incomplete'; - SNewLookupFieldCaption = 'New Lookup Field'; - SDataSourceChange = 'DataSource cannot be changed'; - SNoNestedMasterSource = 'Nested datasets cannot have a MasterSource'; - SDataSetOpen = 'Cannot perform this operation on an open dataset'; - SNotEditing = 'Dataset not in edit or insert mode'; - SDataSetClosed = 'Cannot perform this operation on a closed dataset'; - SDataSetEmpty = 'Cannot perform this operation on an empty dataset'; - SDataSetReadOnly = 'Cannot modify a read-only dataset'; - SNestedDataSetClass = 'Nested dataset must inherit from %s'; - SExprTermination = 'Filter expression incorrectly terminated'; - SExprNameError = 'Unterminated field name'; - SExprStringError = 'Unterminated string constant'; - SExprInvalidChar = 'Invalid filter expression character: ''%s'''; - SExprNoLParen = '''('' expected but %s found'; - SExprNoRParen = ''')'' expected but %s found'; - SExprNoRParenOrComma = ''')'' or '','' expected but %s found'; - SExprExpected = 'Expression expected but %s found'; - SExprBadField = 'Field ''%s'' cannot be used in a filter expression'; - SExprBadNullTest = 'NULL only allowed with ''='' and ''<>'''; - SExprRangeError = 'Constant out of range'; - SExprNotBoolean = 'Field ''%s'' is not of type Boolean'; - SExprIncorrect = 'Incorrectly formed filter expression'; - SExprNothing = 'nothing'; - SExprTypeMis = 'Type mismatch in expression'; - SExprBadScope = 'Operation cannot mix aggregate value with record-varying value'; - SExprNoArith = 'Arithmetic in filter expressions not supported'; - SExprNotAgg = 'Expression is not an aggregate expression'; - SExprBadConst = 'Constant is not correct type %s'; - SExprNoAggFilter = 'Aggregate expressions not allowed in filters'; - SExprEmptyInList = 'IN predicate list may not be empty'; - SInvalidKeywordUse = 'Invalid use of keyword'; - STextFalse = 'False'; - STextTrue = 'True'; - SParameterNotFound = 'Parameter ''%s'' not found'; - SInvalidVersion = 'Unable to load bind parameters'; - SParamTooBig = 'Parameter ''%s'', cannot save data larger than %d bytes'; - SBadFieldType = 'Field ''%s'' is of an unsupported type'; - SAggActive = 'Property may not be modified while aggregate is active'; - SProviderSQLNotSupported = 'SQL not supported'; - SProviderExecuteNotSupported = 'Execute not supported'; - SExprNoAggOnCalcs = 'Field ''%s'' is not the correct type of calculated field to be used in an aggregate, use an internalcalc'; - SRecordChanged = 'Record not found or changed by another user'; - SDataSetUnidirectional = 'Operation not allowed on a unidirectional dataset'; - SUnassignedVar = 'Unassigned variant value'; - SRecordNotFound = 'Record not found'; - SFileNameBlank = 'FileName property cannot be blank'; - SFieldNameTooLarge = 'Fieldname %s exceeds %d chars'; - -{ For FMTBcd } - - SBcdOverflow = 'BCD overflow'; - SInvalidBcdValue = '%s is not a valid BCD value'; - SInvalidFormatType = 'Invalid format type for BCD'; - -{ For SqlTimSt } - - SCouldNotParseTimeStamp = 'Could not parse SQL TimeStamp string'; - SInvalidSqlTimeStamp = 'Invalid SQL date/time values'; - SCalendarTimeCannotBeRepresented = 'Calendar time cannot be represented'; - - SDeleteRecordQuestion = 'Delete record?'; - SDeleteMultipleRecordsQuestion = 'Delete all selected records?'; - STooManyColumns = 'Grid requested to display more than 256 columns'; - - { For reconcile error } - SSkip = 'Skip'; - SAbort = 'Abort'; - SMerge = 'Merge'; - SCorrect = 'Correct'; - SCancel = 'Cancel'; - SRefresh = 'Refresh'; - SModified = 'Modified'; - SInserted = 'Inserted'; - SDeleted = 'Deleted'; - SCaption = 'Update Error - %s'; - SUnchanged = ''; - SBinary = '(Binary)'; - SAdt = '(ADT)'; - SArray = '(Array)'; - SFieldName = 'Field Name'; - SOriginal = 'Original Value'; - SConflict = 'Conflicting Value'; - SValue = ' Value'; - SNoData = ''; - SNew = 'New'; - -//-----------------------------------------------------------------------// -// DBI types // -//-----------------------------------------------------------------------// - -const - DBIMAXNAMELEN = 63;{31;} { Name limit (table, field etc) } - DBIMAXSPNAMELEN = 64; { Max stored procedure name length } - DBIMAXFLDSINKEY = 16; { Max fields in a key } - DBIMAXKEYEXPLEN = 220; { Max Key expression length } - DBIMAXEXTLEN = 3; { Max file extension len, not incl. dot (excluding zero termination) } - DBIMAXTBLNAMELEN = 260; { Max table name length } - DBIMAXPATHLEN = 260; { Max path+file name len (excluding zero termination) } - DBIMAXMSGLEN = 127; { Max message len } - DBIMAXVCHKLEN = 255; { Max val check len } - DBIMAXPICTLEN = 175; { Max picture len } - DBIMAXFLDSINSEC = 256; { Max fields in security spec } - -Type -//============================================================================// -// G e n e r a l // -//============================================================================// - DBIDATE = Longint; - TIME = Longint; - DBIResult = Word; { function result } - TypedEnum = Integer; - - _hDBIObj = record end; { Dummy structure to create "typed" handles } - hDBIObj = ^_hDBIObj; { Generic object handle } - hDBIDb = ^_hDBIObj; { Database handle } - hDBIStmt = ^_hDBIObj; { Statement handle ("new query") } - hDBICur = ^_hDBIObj; { Cursor handle } - hDBIXact = ^_hDBIObj; { Transaction handle } - hDBIFilter = ^_hDBIObj; { Filter handle } - - -{ Handle Pointers } - phDBIObj = ^hDBIObj; { Pointer to Generic object handle } - phDBIDb = ^hDBIDb; { Pointer to Database handle } - phDBICur = ^hDBICur; { Pointer to Cursor handle } - - -{ typedefs for buffers of various common sizes: } - DBIPATH = packed array [0..DBIMAXPATHLEN] of AnsiDACChar; { holds a DOS path } - DBINAME = packed array [0..DBIMAXNAMELEN] of Char; { holds a name } - DBIEXT = packed array [0..DBIMAXEXTLEN] of PAnsiDACChar; { holds an extension EXT } - DBITBLNAME = packed array [0..DBIMAXTBLNAMELEN] of AnsiDACChar; { holds a table name } - DBISPNAME = packed array [0..DBIMAXSPNAMELEN] of AnsiDACChar; { holds a stored procedure name } - DBIKEY = packed array [0..DBIMAXFLDSINKEY-1] of Word; { holds list of fields in a key } - DBIKEYEXP = packed array [0..DBIMAXKEYEXPLEN] of AnsiDACChar; { holds a key expression } - DBIVCHK = packed array [0..DBIMAXVCHKLEN] of Byte; { holds a validity check } - DBIPICT = packed array [0..DBIMAXPICTLEN] of AnsiDACChar; { holds a picture (Pdox) } - DBIMSG = packed array [0..DBIMAXMSGLEN] of AnsiDACChar; { holds an error message } - -{============================================================================} -{ Statement parameter information } -{============================================================================} - -type - STMTParamType = ( - paramUNKNOWN, { UNKNOWN (Error) } - paramIN, { Input parameter } - paramOUT, { Output parameter } - paramINOUT, { Input/Output parameter } - paramRET { procedure (or function) return } - ); - -//============================================================================// -// General properties DbiGetProp/DbiSetProp // -//============================================================================// -{ Cursor properties } -{ General } - -const - curMAXPROPS = $00050000; { ro UINT16 , Number of defined properties } - curTABLELEVEL = $00050003; { ro UINT16 , Table level 1..n } - curXLTMODE = $00050005; { rw XLTMode , Translate mode } - curMAXFIELDID = $0005000F; { ro UINT16, Max # of field desc } - curFIELDFULLNAME = $00050010; { ro pObjAttrDesc, Object attribute name } - curFIELDTYPENAME = $00050011; { ro pObjTypeDesc, Object Type name } - curMAKECRACK = $00050014; { Create a crack at the current cursor position } - curFIELDISAUTOINCR = $00050015; { wo BOOL, Auto increment field } - curFIELDISDEFAULT = $00050016; { wo BOOL, Default field } - curAUTOREFETCH = $00050017; { rw BOOL, Refetch inserted record } - - maxcurPROPS = 23; { keep in sync when adding cursor properties } - -{ SQL Driver specific } - curUPDLOCKMODE = $04050000; { rw UPDLockMode, Update lock mode } - curGETHIDDENCOLUMNS= $04050004; { rw BOOL , Get all selected columns from server. } -{ Delayed Updates Specific. } - curDELAYUPDDISPLAYOPT = $05050003; { rw UINT16, view records } - curDELAYUPDGETOLDRECORD = $05050004; { rw BOOL, get un-modified } - curDELAYUPDNUMUPDATES = $05050005; { ro INT32, num of updates } -{ Database properties } -{ General } - dbDATABASETYPE = $00040002; { ro pDBINAME , Database type } - dbPARAMFMTQMARK = $00040004; { rw BOOL , Stmt param marker fmt = ? } - dbUSESCHEMAFILE = $00040005; { rw BOOL , for text driver only. } - -{ SQL Driver specific } - dbCOMPRESSARRAYFLDDESC = $04040011; { rw BOOL, VARRAY in compressed format, ORACLE 8 specific. } - -{ Statement properties } -{ General } - stmtUNIDIRECTIONAL = $00060010; { rw BOOL Cursor Unidirectional } - stmtROWCOUNT = $00060014; { ro UINT32 Rows effected by a stmt } - -{ specific to QBE or local SQL } - stmtLIVENESS = $00060021; { rw LIVENESS Preference for canned/live answers } - stmtAUXTBLS = $00060026; { rw BOOL True if QBE to create CHANGED, etc. } - stmtCANNEDREADONLY = $00060042; { rw BOOL canned answers are readonly } - -//============================================================================// -// Transactions // -//============================================================================// -type - eXILType = ( { Transaction isolation levels } - xilDIRTYREAD, { Uncommitted changes read } - xilREADCOMMITTED, { Committed changes, no phantoms } - xilREPEATABLEREAD, { Full read repeatability } - xilSERIALIZABLE { SERIALIZABLE } - ); - - eXEnd = ( { Transaction end control } - xendCOMMIT, { Commit transaction } - xendCOMMITKEEP, { Commit transaction, keep cursors } - xendABORT { Rollback transaction } - ); - - eXState = ( { Transaction end control } - xsINACTIVE, { Transaction inactive } - xsACTIVE { Transaction active } - ); - - pXInfo = ^XInfo; - XInfo = packed record - exState : eXState; { xsActive, xsInactive } - eXIL : eXILType; { Xact isolation level } - uNests : Word; { Xact children } - end; - -//============================================================================// -// Object types // -//============================================================================// - -type - pObjTypeDesc = ^ObjTypeDesc; - ObjTypeDesc = packed record - iFldNum : Word; { Field id } - szTypeName : DBINAME; { Object type name } - end; - - - -//============================================================================// -// Cursor properties // -//============================================================================// - -type - DBIShareMode = ( { Database/Table Share type } - dbiOPENSHARED, { Open shared (Default) } - dbiOPENEXCL { Open exclusive } - ); - - DBIOpenMode = ( { Database/Table Access type } - dbiREADWRITE, { Read + Write (Default) } - dbiREADONLY { Read only } - ); - - DBILockType = ( { Lock types (Table level) } - dbiNOLOCK, { No lock (Default) } - dbiWRITELOCK, { Write lock } - dbiREADLOCK { Read lock } - ); - - XLTMode = ( { Field translate mode } - xltNONE, { No translation (Physical Types) } - xltRECORD, { Record level translation (not supported) } - xltFIELD { Field level translation (Logical types) } - ); - - pServerColDesc = ^ServerColDesc; - ServerColDesc = packed record { Auto increment and Defaults property } - iFldNum : Word; { Field id } - bServerCol : WordBool; { Auto Increment and Default } - end; - - -type - pCURProps = ^CURProps; - CURProps = packed record { Virtual Table properties } - szName : DBITBLNAME; { table name (no extension, if it can be derived) } - iFNameSize : integer; { Full file name size } - szTableType : DBINAME; { Driver type } - iFields : integer; { No of fields in Table } - iRecSize : integer; { Record size (logical record) } - iRecBufSize : integer; { Record size (physical record) } - iKeySize : integer; { Key size } - iIndexes : integer; { Number of indexes } - iValChecks : integer; { Number of val checks } - iRefIntChecks : integer; { Number of Ref Integrity constraints } - iBookMarkSize : integer; { Bookmark size } - bBookMarkStable : WordBool; { Stable book marks } - eOpenMode : DBIOpenMode; { ReadOnly / RW } - eShareMode : DBIShareMode; { Excl / Share } - bIndexed : WordBool; { Index is in use } - iSeqNums : SmallInt; { 1: Has Seqnums; 0: Has Record# } - bSoftDeletes : WordBool; { Supports soft deletes } - bDeletedOn : WordBool; { if above, deleted recs seen } - iRefRange : integer; { Not used } - exltMode : XLTMode; { Translate Mode } - iRestrVersion : integer; { Restructure version number } - bUniDirectional : WordBool; { Cursor is uni-directional } - Dummy4 : Word; - iFmlRights : integer; { Family rights } - iPasswords : integer; { Number of Aux passwords } - iCodePage : integer; { Codepage (0 if unknown) } - bProtected : WordBool; { Table is protected by password } - iTblLevel : integer; { Driver dependent table level } - szLangDriver : DBINAME; { Language driver name } - bFieldMap : WordBool; { Field map active } - iBlockSize : integer; { Physical file blocksize in K } - bStrictRefInt : WordBool; { Strict referential integrity } - iFilters : integer; { Number of filters } - bTempTable : WordBool; { Table is a temporary table } - iUnUsed : packed array [0..15] of Word; - end; - -//Delayed Update Types and Constants } - -type - DBIDelayedUpdCmd = ( { Op types for Delayed Update cursor } - dbiDelayedUpdCommit, { Commit the updates } - dbiDelayedUpdCancel, { Rollback the updates } - dbiDelayedUpdCancelCurrent, { Cancel the Current Rec Change } - dbiDelayedUpdPrepare { Phase1 of 2 phase commit } - ); - -//============================================================================// -// Record Properties // -//============================================================================// - -type - pRECProps = ^RECProps; - RECProps = packed record { Record properties } - iSeqNum : Longint; { When Seq# supported only } - iPhyRecNum : Longint; { When Phy Rec#s supported only } - iRecStatus : Word; { Delayed Updates Record Status } - bSeqNumChanged : WordBool; { Not used } - bDeleteFlag : WordBool; { When soft delete supported only } - end; - -//============================================================================// -// Index descriptor // -//============================================================================// - -type - pIDXDesc = ^IDXDesc; - IDXDesc = record { Index description } - szName : string; { Index name } - iIndexId : integer; { Index number } - szFormat : string; { Optional format (BTREE, HASH etc) } - bPrimary : WordBool; { True, if primary index } - bUnique : WordBool; { True, if unique keys (TRI-STATE for dBASE) } - bDescending : WordBool; { True, for descending index } - bMaintained : WordBool; { True, if maintained index } - bSubset : WordBool; { True, if subset index } - bExpIdx : WordBool; { True, if expression index } - iCost : integer; { Not used } - iFldsInKey : integer; { Fields in the key (1 for Exp) } - iKeyLen : integer; { Phy Key length in bytes (Key only) } - bOutofDate : WordBool; { True, if index out of date } - iKeyExpType : integer; { Key type of Expression } - aiKeyFld : DBIKEY; { Array of field numbers in key } - szKeyExp : string; { Key expression } - szKeyCond : string; { Subset condition } - bCaseInsensitive : WordBool; { True, if case insensitive index } - iBlockSize : integer; { Block size in bytes } - iRestrNum : integer; { Restructure number } - abDescending : packed array [0..DBIMAXFLDSINKEY-1] of WordBool; { TRUE } - iUnUsed : packed array [0..15] of Word; - end; - -//============================================================================// -// Table / Field Types // -//============================================================================// -const -{ Field Types (Logical) } - fldUNKNOWN = 0; - fldZSTRING = 1; { Null terminated string } - fldDATE = 2; { Date (32 bit) } - fldBLOB = 3; { Blob } - fldBOOL = 4; { Boolean (16 bit) } - fldINT16 = 5; { 16 bit signed number } - fldINT32 = 6; { 32 bit signed number } - fldFLOAT = 7; { 64 bit floating point } - fldBCD = 8; { BCD } - fldBYTES = 9; { Fixed number of bytes } - fldTIME = 10; { Time (32 bit) } - fldTIMESTAMP = 11; { Time-stamp (64 bit) } - fldUINT16 = 12; { Unsigned 16 bit integer } - fldUINT32 = 13; { Unsigned 32 bit integer } - fldFLOATIEEE = 14; { 80-bit IEEE float } - fldVARBYTES = 15; { Length prefixed var bytes } - fldLOCKINFO = 16; { Look for LOCKINFO typedef } - fldCURSOR = 17; { For Oracle Cursor type } - fldINT64 = 18; { 64 bit signed number } - fldUINT64 = 19; { Unsigned 64 bit integer } - fldADT = 20; { Abstract datatype (structure) } - fldARRAY = 21; { Array field type } - fldREF = 22; { Reference to ADT } - fldTABLE = 23; { Nested table (reference) } - {$IFDEF FPC} - fldDATETIME = 24; { DateTime structure field } - {$ENDIF} - {$IFDEF DELPHI_6} - fldDATETIME = 24; { DateTime structure field } - {$IFDEF DELPHI_12} - fldFMTBCD = 25; { BCD Variant type: required by Midas, same as BCD for DBExpress} - fldWIDESTRING = 26; { UCS2 null terminated string } - MAXLOGFLDTYPES = 27; { Number of logical fieldtypes } - {$ELSE} - MAXLOGFLDTYPES = 25; { Number of logical fieldtypes } - {$ENDIF} - {$ELSE} - MAXLOGFLDTYPES = 24; { Number of logical fieldtypes } - {$ENDIF} - - {$IFDEF DELPHI_12} - { Additional (non-BDE fieldtypes } - fldUNICODE = $1007; { Unicode } - {$ENDIF} - - //POSTGRES SPECIFIC - fldTIMESTAMPTZ = MAXLOGFLDTYPES + 1; - fldUUID = MAXLOGFLDTYPES + 2; - fldINET = MAXLOGFLDTYPES + 3; - fldMACADDR = MAXLOGFLDTYPES + 4; - fldPOINT = MAXLOGFLDTYPES + 5; - fldCIRCLE = MAXLOGFLDTYPES + 6; - fldBOX = MAXLOGFLDTYPES + 7; - fldLSEG = MAXLOGFLDTYPES + 8; - fldRANGE = MAXLOGFLDTYPES + 9; - -{ Sub Types (Logical) } - -{ fldFLOAT subtype } - - fldstMONEY = 21; { Money } - -{ fldBLOB subtypes } - - fldstMEMO = 22; { Text Memo } - fldstBINARY = 23; { Binary data } - fldstFMTMEMO = 24; { Formatted Text } - fldstOLEOBJ = 25; { OLE object (Paradox) } - fldstGRAPHIC = 26; { Graphics object } - fldstDBSOLEOBJ = 27; { dBASE OLE object } - fldstTYPEDBINARY = 28; { Typed Binary data } - fldstACCOLEOBJ = 30; { Access OLE object } - fldstHMEMO = 33; { CLOB } - fldstHBINARY = 34; { BLOB } - fldstBFILE = 36; { BFILE } - -{ fldZSTRING subtype } - - fldstPASSWORD = 1; { Password } - fldstFIXED = 31; { CHAR type } - fldstUNICODE = 32; { Unicode } - -{ fldINT32 subtype } - fldstAUTOINC = 29; - -{ fldADT subtype } - - fldstADTNestedTable = 35; { ADT for nested table (has no name) } - -{ fldDATE subtype } - fldstADTDATE = 37; { DATE (OCIDate ) with in an ADT } - -//============================================================================// -// Field descriptor // -//============================================================================// -type - FLDVchk = ( { Field Val Check type } - fldvNOCHECKS, { Does not have explicit val checks } - fldvHASCHECKS, { One or more val checks on the field } - fldvUNKNOWN { Dont know at this time } - ); - - FLDRights = ( { Field Rights } - fldrREADWRITE, { Field can be Read/Written } - fldrREADONLY, { Field is Read only } - fldrNONE, { No Rights on this field } - fldrUNKNOWN { Dont know at this time } - ); - - pFLDDesc = ^FLDDesc; - FLDDesc = packed record { Field Descriptor } - iFldNum : integer; { Field number (1..n) } - iNativeType : cardinal; { Field native type } - szName : string; { Field name } - iFldType : integer; { Field type } - iSubType : integer; { Field subtype (if applicable) } - iUnits1 : integer; { Number of Chars, digits etc } - iUnits2 : integer; { Decimal places etc. } - iOffset : integer; { Offset in the record (computed) } - iLen : integer; { Length in bytes (computed) } - iNullOffset : integer; { For Null bits (computed) } - efldvVchk : FLDVchk; { Field Has vcheck (computed) } - efldrRights : FLDRights; { Field Rights (computed) } - bCalcField : WordBool; { Is Calculated field (computed) } - iUnUsed : packed array [0..1] of integer; - end; - - TFLDDescList = array of FLDDesc; - - TIDXDescList = array of IDXDesc; - -//============================================================================// -// Validity check, Referential integrity descriptors // -//============================================================================// -// Subtypes for Lookup - LKUPType = ( { Paradox Lookup type } - lkupNONE, { Has no lookup } - lkupPRIVATE, { Just Current Field + Private } - lkupALLCORRESP, { All Corresponding + No Help } - lkupHELP, { Just Current Fld + Help and Fill } - lkupALLCORRESPHELP { All Corresponging + Help } - ); - - pVCHKDesc = ^VCHKDesc; - VCHKDesc = packed record { Val Check structure } - iFldNum : Word; { Field number } - bRequired : WordBool; { if True, value is required } - bHasMinVal : WordBool; { if True, has min value } - bHasMaxVal : WordBool; { if True, has max value } - bHasDefVal : WordBool; { if True, has default value } - aMinVal : DBIVCHK; { Min Value } - aMaxVal : DBIVCHK; { Max Value } - aDefVal : string; { Default value } - szPict : DBIPICT; { Picture string } - elkupType : LKUPType; { Lookup/Fill type } - szLkupTblName : string; { Lookup Table name } - end; - -//============================================================================// -// Miscellaneous // -//============================================================================// - -{ Index Id used to open table without a default index (i.e. no order) } -const - NODEFAULTINDEX = $FFFF; - - -//============================================================================// -// BookMark compares // -//============================================================================// - -type - PCMPBkMkRslt = ^CMPBkMkRslt; - CMPBkMkRslt = TypedEnum; -const - CMPLess = -1; { Bkm1 < Bkm2 } - CMPEql = 0; { BookMarks are exactly the same } - CMPGtr = 1; { Bkm1 > Bkm2 } - CMPKeyEql = 2; { Only Bkm1.key_val = Bkm2.key_val } - - -{============================================================================} -{ Key searches } -{============================================================================} - -type - DBISearchCond = ( { Search condition for keys } - keySEARCHEQ, { = } - keySEARCHGT, { > } - keySEARCHGEQ { >= } - ); - -//============================================================================// -// Filter description // -//============================================================================// - -type - pCANOp = ^CANOp; - CANOp = ( - canNOTDEFINED, { (*) } - canISBLANK, { CANUnary; is operand blank. (*) } - canNOTBLANK, { CANUnary; is operand not blank. (*) } - canEQ, { CANBinary, CANCompare; equal. (*) } - canNE, { CANBinary; NOT equal. (*) } - canGT, { CANBinary; greater than. (*) } - canLT, { CANBinary; less than. (*) } - canGE, { CANBinary; greater or equal. (*) } - canLE, { CANBinary; less or equal. (*) } - canNOT, { CANUnary; NOT (*) } - canAND, { CANBinary; AND (*) } - canOR, { CANBinary; OR (*) } - canTUPLE2, { CANUnary; Entire record is operand. } - canFIELD2, { CANUnary; operand is field (*) } - canCONST2, { CANUnary; operand is constant (*) } - canMINUS, { CANUnary; minus. } - canADD, { CANBinary; addition. } - canSUB, { CANBinary; subtraction. } - canMUL, { CANBinary; multiplication. } - canDIV, { CANBinary; division. } - canMOD, { CANBinary; modulo division. } - canREM, { CANBinary; remainder of division. } - canSUM, { CANBinary, accumulate sum of. } - canCOUNT, { CANBinary, accumulate count of. } - canMIN, { CANBinary, find minimum of. } - canMAX, { CANBinary, find maximum of. } - canAVG, { CANBinary, find average of. } - canCONT, { CANBinary; provides a link between two } - canUDF2, { CANBinary; invokes a User defined fn } - canCONTINUE2, { CANUnary; Stops evaluating records } - canLIKE, { CANCompare, extended binary compare (*) } - canIN, { CANBinary field in list of values } - canLIST2, { List of constant values of same type } - canUPPER, { CANUnary: upper case } - canLOWER, { CANUnary: lower case } - canFUNC2, { CANFunc: function } - canLISTELEM2, { CANListElem: List Element } - canASSIGN { CANBinary: Field assignment } - ); - - NODEClass = ( { Node Class } - nodeNULL, { Null node (*) } - nodeUNARY, { Node is a unary (*) } - nodeBINARY, { Node is a binary (*) } - nodeCOMPARE, { Node is a compare (*) } - nodeFIELD, { Node is a field (*) } - nodeCONST, { Node is a constant (*) } - nodeTUPLE, { Node is a record } - nodeCONTINUE, { Node is a continue node (*) } - nodeUDF, { Node is a UDF node } - nodeLIST, { Node is a LIST node } - nodeFUNC, { Node is a function node } - nodeLISTELEM { Node is a List Element node } - ); - -// NODE definitions including misc data structures // -//-------------------------------------------------// - -type - pCANHdr = ^CANHdr; - CANHdr = packed record { Header part common to all (*) } - nodeClass : NODEClass; - canOp : CANOp; - end; - - pCANUnary = ^CANUnary; - CANUnary = packed record { Unary Node (*) } - nodeClass : NODEClass; - canOp : CANOp; - iOperand1 : Word; { Byte offset of Operand node } - end; - - pCANBinary = ^CANBinary; - CANBinary = packed record { Binary Node (*) } - nodeClass : NODEClass; - canOp : CANOp; - iOperand1 : Word; { Byte offset of Op1 } - iOperand2 : Word; { Byte offset of Op2 } - end; - - pCANField = ^CANField; - CANField = packed record { Field } - nodeClass : NODEClass; - canOp : CANOp; - iFieldNum : Word; - iNameOffset : Word; { Name offset in Literal pool } - end; - - pCANConst = ^CANConst; - CANConst = packed record { Constant } - nodeClass : NODEClass; - canOp : CANOp; - iType : Word; { Constant type. } - iSize : Word; { Constant size. (in bytes) } - iOffset : Word; { Offset in the literal pool. } - end; - - pCANTuple = ^CANTuple; - CANTuple = packed record { Tuple (record) } - nodeClass : NODEClass; - canOp : CANOp; - iSize : Word; { Record size. (in bytes) } - end; - - pCANContinue = ^CANContinue; - CANContinue = packed record { Break Node (*) } - nodeClass : NODEClass; - canOp : CANOp; - iContOperand : Word; { Continue if operand is true. } - end; - - pCANCompare = ^CANCompare; - CANCompare = packed record { Extended compare Node (text fields) (*) } - nodeClass : NODEClass; - canOp : CANOp; { canLIKE, canEQ } - bCaseInsensitive : WordBool; { 3 val: UNKNOWN = "fastest", "native" } - iPartialLen : Word; { Partial fieldlength (0 is full length) } - iOperand1 : Word; { Byte offset of Op1 } - iOperand2 : Word; { Byte offset of Op2 } - end; - - pCANFunc = ^CANFunc; - CANFunc = packed record { function } - nodeClass : NODEClass; - canOp : CANOp; - iNameOffset : Word; { Name offset in Literal pool } - iElemOffset : Word; { Offset of first List Element in Node pool } - end; - - pCANListElem = ^CANListElem; - CANListElem = packed record { List Element } - nodeClass : NODEClass; - canOp : CANOp; - iOffset : Word; { Arg offset in Node pool } - iNextOffset : Word; { Offset in Node pool of next ListElem or 0 if end of list } - end; - - pCANList = ^CANList; - CANList = packed record { List of Constants } - nodeClass : NODEClass; - canOp : CANOp; - iType : Word; { Constant type. } - iTotalSize : Word; { Total list size; } - iElemSize : Word; { Size of each elem for fix-width types } - iElems : Word; { Number of elements in list } - iOffset : Word; { Offset in the literal pool to first elem. } - end; - - pCANNode = ^CANNode; - CANNode = packed record - case Integer of - 0: (canHdr : CANHdr); - 1: (canUnary : CANUnary); - 2: (canBinary : CANBinary); - 3: (canField : CANField); - 4: (canConst : CANConst); - 5: (canTuple : CANTuple); - 6: (canContinue : CANContinue); - 7: (canCompare : CANCompare); - 8: (canList : CANList); - 9: (canFunc : CANFunc); - 10: (canListElem : CANListElem); - end; - -type - ppCANExpr = ^pCANExpr; - pCANExpr = ^CANExpr; - CANExpr = packed record { Expression Tree } - iVer : Word; { Version tag of expression. } - iTotalSize : Word; { Size of this structure } - iNodes : Word; { Number of nodes } - iNodeStart : Word; { Starting offet of Nodes in this } - iLiteralStart : Word; { Starting offset of Literals in this } - end; - - pfGENFilter = function ( - ulClientData : Longint; - pRecBuf : Pointer; - iPhyRecNum : Longint - ): SmallInt; stdcall; - -//----------------------------------------------------------------------------// -// DBI Query related types // -//----------------------------------------------------------------------------// - - LIVENESS = ( - wantDEFAULT, { Default , same as wantCANNED } - wantLIVE, { Want live data even if extra effort (no guarantee) } - wantCANNED, { Want canned data even if extra effort (guaranteed) } - wantSPEED { Let query manager decide, find out afterwards } - ); - -{======================================================================} -{ Stored procedure and Stored procedure Param descriptor } -{======================================================================} - pSPParamDesc = ^SPParamDesc; - SPParamDesc = packed record - uParamNum : Word; - szName : string; - eParamType : STMTParamType; - uFldType : Word; - uSubType : Word; - iUnits1 : SmallInt; - iUnits2 : SmallInt; - uOffset : Word; - uLen : Word; - uNullOffset : Word; - end; - -//============================================================================// -// Call Backs // -//============================================================================// -type - pCBType = ^CBType; - CBType = ( { Call back type } - cbGENERAL, { General purpose } - cbRESERVED1, - cbRESERVED2, - cbINPUTREQ, { Input requested } - cbRESERVED4, - cbRESERVED5, - cbBATCHRESULT, { Batch processing rslts } - cbRESERVED7, - cbRESTRUCTURE, { Restructure } - cbRESERVED9, - cbRESERVED10, - cbRESERVED11, - cbRESERVED12, - cbRESERVED13, - cbRESERVED14, - cbRESERVED15, - cbRESERVED16, - cbRESERVED17, - cbTABLECHANGED, { Table changed notification } - cbRESERVED19, - cbCANCELQRY, { Allow user to cancel Query } - cbSERVERCALL, { Server Call } - cbRESERVED22, - cbGENPROGRESS, { Generic Progress report. } - cbDBASELOGIN, { dBASE Login } - cbDELAYEDUPD, { Delayed Updates } - cbFIELDRECALC, { Field(s) recalculation } - cbTRACE, { Trace } - cbDBLOGIN, { Database login } - cbDETACHNOTIFY, { DLL Detach Notification } - cbNBROFCBS { Number of cbs } - ); - -type - pCBRType = ^CBRType; - CBRType = ( { Call-back return type } - cbrUSEDEF, { Take default action } - cbrCONTINUE, { Continue } - cbrABORT, { Abort the operation } - cbrCHKINPUT, { Input given } - cbrYES, { Take requested action } - cbrNO, { Do not take requested action } - cbrPARTIALASSIST, { Assist in completing the job } - cbrSKIP, { Skip this operation } - cbrRETRY { Retry this operation } - ); - - ppfDBICallBack = ^pfDBICallBack; - pfDBICallBack = function ( { Call-back funtion pntr type } - ecbType : CBType; { Callback type } - iClientData : Longint; { Client callback data } - CbInfo : Pointer { Call back info/Client Input } - ): CBRType; stdcall; - - DelayUpdErrOpType = ( { type of delayed update object (delayed updates callback) } - delayupdNONE, - delayupdMODIFY, - delayupdINSERT, - delayupdDELETE - ); - - PDELAYUPDCbDesc = ^DELAYUPDCbDesc; - DELAYUPDCbDesc = packed record { delayed updates callback info } - iErrCode : DBIResult; - eDelayUpdOpType : DelayUpdErrOpType; - iRecBufSize : Word; { Record size (physical record) } - pNewRecBuf : Pointer; - pOldRecBuf : Pointer; - end; - - - -///////////////////////////////////////////////////////////////////////////// -// CONSTANTS DEFINITION // -///////////////////////////////////////////////////////////////////////////// -type - TFieldArray = array[0..255] of Integer; - TTrueArray = Set of AnsiDACByteChar; - TFalseArray = Set of AnsiDACByteChar; - -///////////////////////////////////////////////////////////////////////////// -// TPgSQLFilter TYPES AND CONST // -///////////////////////////////////////////////////////////////////////////// -type - TFldType = (FT_UNK, FT_INT, FT_DATETIME, FT_DATE, FT_TIME, FT_CURRENCY, FT_FLOAT, FT_STRING, FT_BOOL); - - StrRec = record - allocSiz : Longint; - refCnt : Longint; - length : Longint; - end; - -const - strsz = sizeof(StrRec); - -///////////////////////////////////////////////////////////////////////////// -// INDEX AND PRIMARY KEY DEFINITIONS // -///////////////////////////////////////////////////////////////////////////// -type - - TPropRec = Record - Prop : Word; - Group : Word; - end; - - TBlobItem = Record - Blob : TMemoryStream; - end; - - PPSQLBookMark = ^TPSQLBookMark; - TPSQLBookMark = Record - Position : Int64;//Longint; - end; - - PFieldStatus = ^TFieldStatus; - TFieldStatus = Record - isNULL : SmallInt; - Changed : LongBool; - end; - - TRecordState = (tsNoPos, tsPos, tsFirst, tsLast, tsEmpty, tsClosed); - TDir = (tdUndefined, tdNext, tdPrev); - - TSSLMode = (sslDisable , sslAllow, sslPrefer, sslRequire, sslVerifyCA, sslVerifyFull); - - TPSQLDatasetOption = (dsoByteaAsEscString, dsoOIDAsInt, dsoForceCreateFields, - dsoUseGUIDField, dsoTrimCharFields, dsoPopulateFieldsOrigin, - dsoManageLOFields, dsoEmptyCharAsNull, dsoUDTAsMaxString, - dsoRefreshModifiedRecordOnly, dsoFetchOnDemand, dsoNumericAsFloat); - - TPSQLDatasetOptions = set of TPSQLDatasetOption; - -const - SSLConsts: array[TSSLMode] of string = ('disable' , 'allow', 'prefer', - 'require', 'verify-ca', 'verify-full'); - - SSLOpts: array[0..3] of string = ('sslcert', 'sslkey', 'sslrootcert', 'sslcrl'); - -type - TDBOptions = record - User : String; - Password : String; - DatabaseName : String; - Port : Cardinal; - Host : String; - SSLMode : string; - ConnectionTimeout: cardinal; - end; - - - PPGFIELD_INFO = ^TPGFIELD_INFO; - TPGField_Info = record - FieldIndex : Integer; - FieldName : String; - FieldType : cardinal; - FieldSize : Integer; - FieldMaxSize : Integer; - FieldDefault : String; - FieldNotNull : boolean; - FieldTypMod : Integer; - end; - -///////////////////////////////////////////////////////////////////////////// -// BASE OBJECTS DEFINITIONS // -///////////////////////////////////////////////////////////////////////////// - {TContainer Object} - TContainer = Class(TObject) - private - FItems: TList{$IFDEF NEXTGEN}{$ENDIF}; - public - constructor Create; - Destructor Destroy; Override; - function At(Index: integer): Pointer; - procedure AtDelete(Index: integer); - procedure AtInsert(Index: integer; Item: Pointer); - procedure AtPut(Index: integer; Item: Pointer); - procedure Clear; - procedure Delete(Item: Pointer); - procedure DeleteAll; - procedure FreeAll; - function Get(AIndex: integer): Pointer; - function GetCount: integer; - function IndexOf(Item: Pointer): integer; - procedure Insert(Item: Pointer); Virtual; - procedure Pack; - procedure Put(AIndex: integer; APointer: Pointer); - function GetCapacity: integer; - procedure SetCapacity(NewCapacity: integer); - property Count: integer Read GetCount; - property Items[index: integer]: Pointer Read Get Write Put; - property Capacity: integer Read GetCapacity Write SetCapacity; - end; - - TBaseObject = Class(TObject) - Protected - FParent : TObject; - FContainer: TContainer; - Public - property Container : TContainer Read FContainer Write FContainer; - property Parent : TObject Read FParent Write FParent; - constructor Create(P : TObject; Container : TContainer); - Destructor Destroy; Override; - end; - -///////////////////////////////////////////////////////////////////////////// -// COMMON FUNCTIONS // -///////////////////////////////////////////////////////////////////////////// -{ SQL Parser } -type - TSQLToken = (stUnknown, stTableName, stFieldName, stAscending, stDescending, stSelect, - stFrom, stWhere, stGroupBy, stHaving, stUnion, stPlan, stOrderBy, stForUpdate, - stEnd, stPredicate, stValue, stIsNull, stIsNotNull, stLike, stAnd, stOr, - stNumber, stAllFields, stComment, stDistinct,stSubSelect,stFunction,stAliace,stAs); - -const - SQLSections = [stSelect, stFrom, stWhere, stGroupBy, stHaving, stUnion, - stPlan, stOrderBy, stForUpdate]; -{$IFDEF DELPHI_4} - curAUTOREFETCH = $00050017; { rw BOOL, Refetch inserted record } -{$ENDIF} - -function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; -function GetTable(const SQL: String; var Aliace : String): String; -function SqlDateToDateTime(Value: string; const IsTime: boolean): TDateTime; -function DateTimeToSqlDate(Value: TDateTime; Mode : integer): string; -function SQLTimeStampToDateTime(Value: string): TDateTime; -function StrToSQLFloat(Value: string): Double; -function SQLFloatToStr(Value: Double): string; - -procedure GetToken(var Buffer, Token: string); -procedure ConverPSQLtoDelphiFieldInfo(Info : TPGFIELD_INFO; Count, Offset : integer; - var RecBuff : FLDDesc; - var ValChk : VCHKDesc; - var LocArray : Boolean); - -procedure LoadPSQLLibrary(LibPQPath: string = ''); -procedure UnloadPSQLLibrary; -procedure CheckLibraryLoaded; -procedure HotLibrarySwitch(const ALibPath: string); -function IsLibraryLoaded: boolean; - -function IsValidIP(const S: string): boolean; - -function MaskSearch(const Str, Mask: string; - CaseSensitive : boolean = true; - MaskChar: Char = '?'; - WildCard: Char = '%'): Boolean; - -function Search(Op1,Op2 : Variant; OEM, CaseSen : Boolean; PartLen: Integer):Boolean; -function GetBDEErrorMessage(ErrorCode : Word):String; -procedure FieldMapping(FieldType : cardinal; phSize : Integer; var BdeType : integer; - var BdeSubType : integer; var LogSize : Integer; - var LocArray : Boolean); - -{$IFNDEF DELPHI_16} -function UIntToStr(C: cardinal): string; -{$ENDIF} -function StrToUInt(S: string): cardinal; -function StrToUIntDef(S: string; DefVal: cardinal = 0): cardinal; - -function DeBracketedStr(const Value: string; ALeftBracket: char = '('; ARightBracket: char = ')'): string; - -{$IFNDEF DELPHI_12} -type - TCharSet = set of char; - TRecordBuffer = PAnsiChar; - - function CharInSet(C: Char; const CharSet: TCharSet): Boolean; -{$ENDIF} - -{$IFDEF NEXTGEN} -type - TRecordBuffer = PByte; -{$ENDIF NEXTGEN} - -{$IFNDEF DELPHI_12} - - {$IFDEF DELPHI_5} - function Utf8Encode(const WS: WideString): AnsiString; - {$ENDIF} - - function UTF8ToString(const S: String): string; -{$ENDIF} - -{$IFDEF DELPHI_5} -function GetModuleName(Module: HMODULE): string; -function Sign(const AValue: Double): integer; -{$ENDIF} - - -//function for compatibility with FreePascal and MacOS -{$IFNDEF MSWINDOWS} -procedure ZeroMemory(Destination: Pointer; Length: integer); -procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: integer); -{$ENDIF MSWINDOWS} -{$IFDEF MACOS} -function GetTickCount: LongWord; //thanks to Indy project -{$ELSE} -function GetTickDiff(const AOldTickCount, ANewTickCount: LongWord): LongWord; -{$ENDIF} - -function DACAnsiStrAlloc(Size: Cardinal): PAnsiDACChar; -procedure DACAnsiStrDispose(Str: PAnsiDACChar); - -function DACStrCopy(Dest: PAnsiDACChar; const Source: PAnsiDACChar; MaxLen: Cardinal = 0): PAnsiDACChar; -procedure DACAllocStr(var Dest: PAnsiDACChar; Len: Integer); - -{$IFDEF MOBILE} -function DACAnsiStrBufSize(const Str: PAnsiDACChar): Cardinal; -function DACStrBufSize(const Str: PAnsiDACChar): Cardinal; -{$ENDIF} - -function GetTickCount: LongWord; //thanks to Indy project - - -implementation - -uses PSQLDbTables, PSQLAccess - {$IFDEF DELPHI_6}, StrUtils{$ENDIF} - {$IFDEF FPC}, StrUtils{$ENDIF} - {$IFDEF NEXTGEN}, Character{$ENDIF}; - -type - T4 = 0..3; - T8 = 0..7; - TIPv4ByteArray = array[T4] of Byte; - TIPv6WordArray = array[T8] of Word; - - TIPv4 = packed record - case Integer of - 0: (D, C, B, A: Byte); - 1: (Groups: TIPv4ByteArray); - 2: (Value: Cardinal); - end; - - TIPv6 = packed record - case Integer of - 0: (H, G, F, E, D, C, B, A: Word); - 1: (Groups: TIPv6WordArray); - end; - -{$IFDEF UNDER_DELPHI_6} -function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer; -var - I,X: Integer; - Len, LenSubStr: Integer; -begin - if Offset = 1 then - Result := Pos(SubStr, S) - else - begin - I := Offset; - LenSubStr := Length(SubStr); - Len := Length(S) - LenSubStr + 1; - while I <= Len do - begin - if S[I] = SubStr[1] then - begin - X := 1; - while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do - Inc(X); - if (X = LenSubStr) then - begin - Result := I; - exit; - end; - end; - Inc(I); - end; - Result := 0; - end; -end; - -function TryStrToInt(const S: string; out V: integer): boolean; -var Code: integer; -begin - Val(S, V, Code); - Result := Code = 0; -end; -{$ENDIF UNDER_DELPHI_6} - -function TryStrToIPv4(const S: String; out Value: TIPv4): boolean; -var - SIP: String; - Start: Integer; - I: T4; - Index: Integer; - Count: Integer; - SGroup: String; - G: Integer; -begin - Result := False; - SIP := S + '.'; - Start := 1; - for I := High(T4) downto Low(T4) do - begin - Index := PosEx('.', SIP, Start); - if Index = 0 then - Exit; - Count := Index - Start + 1; - SGroup := Copy(SIP, Start, Count - 1); - if TryStrToInt(SGroup, G) and (G >= Low(Byte)) and (G < High(Byte)) then - Value.Groups[I] := G - else - Exit; - Inc(Start, Count); - end; - Result := True; -end; - -function TryStrToIPv6(const S: String; out Value: TIPv6): boolean; -{ Valid examples for S: - 2001:0db8:85a3:0000:0000:8a2e:0370:7334 - 2001:db8:85a3:0:0:8a2e:370:7334 - 2001:db8:85a3::8a2e:370:7334 - ::8a2e:370:7334 - 2001:db8:85a3:: - ::1 - :: - ::ffff:c000:280 - ::ffff:192.0.2.128 } -var - ZeroPos: Integer; - DotPos: Integer; - SIP: String; - Start: Integer; - Index: Integer; - Count: Integer; - SGroup: String; - G: Integer; - - procedure NormalNotation; - var - I: T8; - begin - SIP := S + ':'; - Start := 1; - for I := High(T8) downto Low(T8) do - begin - Index := PosEx(':', SIP, Start); - if Index = 0 then - Exit; - Count := Index - Start + 1; - SGroup := '$' + Copy(SIP, Start, Count - 1); - if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then - Exit; - Value.Groups[I] := G; - Inc(Start, Count); - end; - Result := True; - end; - - procedure CompressedNotation; - var - I: T8; - A: array of Word; - begin - SIP := S + ':'; - Start := 1; - I := High(T8); - while Start < ZeroPos do - begin - Index := PosEx(':', SIP, Start); - if Index = 0 then - Exit; - Count := Index - Start + 1; - SGroup := '$' + Copy(SIP, Start, Count - 1); - if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then - Exit; - Value.Groups[I] := G; - Inc(Start, Count); - Dec(I); - end; - FillChar(Value.H, (I + 1) * SizeOf(Word), 0); - if ZeroPos < (Length(S) - 1) then - begin - SetLength(A, I + 1); - Start := ZeroPos + 2; - repeat - Index := PosEx(':', SIP, Start); - if Index > 0 then - begin - Count := Index - Start + 1; - SGroup := '$' + Copy(SIP, Start, Count - 1); - if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then - Exit; - A[I] := G; - Inc(Start, Count); - Dec(I); - end; - until Index = 0; - Inc(I); - Count := Length(A) - I; - Move(A[I], Value.H, Count * SizeOf(Word)); - end; - Result := True; - end; - - procedure DottedQuadNotation; - var - I: T4; - begin - if UpperCase(Copy(S, ZeroPos + 2, 4)) <> 'FFFF' then - Exit; - FillChar(Value.E, 5 * SizeOf(Word), 0); - Value.F := $FFFF; - SIP := S + '.'; - Start := ZeroPos + 7; - for I := Low(T4) to High(T4) do - begin - Index := PosEx('.', SIP, Start); - if Index = 0 then - Exit; - Count := Index - Start + 1; - SGroup := Copy(SIP, Start, Count - 1); - if not TryStrToInt(SGroup, G) or (G > High(Byte)) or (G < 0) then - Exit; - case I of - 0: Value.G := G shl 8; - 1: Inc(Value.G, G); - 2: Value.H := G shl 8; - 3: Inc(Value.H, G); - end; - Inc(Start, Count); - end; - Result := True; - end; - -begin - Result := False; - ZeroPos := Pos('::', S); - if ZeroPos = 0 then - NormalNotation - else - begin - DotPos := Pos('.', S); - if DotPos = 0 then - CompressedNotation - else - DottedQuadNotation; - end; -end; - -function IsValidIP(const S: string): boolean; -var IP4: TIPv4; - IP6: TIPv6; -begin - Result := TryStrToIPv4(S, IP4) or TryStrToIPv6(S, IP6); -end; - -{$IFNDEF MSWINDOWS} -procedure ZeroMemory(Destination: Pointer; Length: integer); -begin - FillChar(Destination^, Length, 0); -end; - -procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: integer); -begin - Move(Source^, Destination^, Length); -end; -{$ENDIF MSWINDOWS} - -{$IFDEF MACOS} -function GetTickCount: LongWord; inline; -begin - Result := AbsoluteToNanoseconds(UpTime) div 1000000; -end; -{$ENDIF} - -function GetTickDiff(const AOldTickCount, ANewTickCount: LongWord): LongWord; -{$IFDEF DELPHI_12}inline;{$ENDIF} -begin - {This is just in case the TickCount rolled back to zero} - if ANewTickCount >= AOldTickCount then begin - Result := ANewTickCount - AOldTickCount; - end else begin - Result := High(LongWord) - AOldTickCount + ANewTickCount; - end; -end; - -{$IFDEF DELPHI_5} -function GetModuleName(Module: HMODULE): string; -var - ModName: array[0..MAX_PATH] of Char; -begin - SetString(Result, ModName, GetModuleFileName(Module, ModName, SizeOf(ModName))); -end; - -function Sign(const AValue: Double): integer; -begin - if ((PInt64(@AValue)^ and $7FFFFFFFFFFFFFFF) = $0000000000000000) then - Result := 0 - else if ((PInt64(@AValue)^ and $8000000000000000) = $8000000000000000) then - Result := -1 - else - Result := 1; -end; -{$ENDIF} - -{$IFNDEF DELPHI_12} - - {$IFDEF DELPHI_5} - function Utf8Encode(const WS: WideString): AnsiString; - begin - Result := WS; - end; - {$ENDIF} - - function UTF8ToString(const S: String): string; - begin - {$IFDEF DELPHI_5} - Result := S; - {$ELSE} - Result := Utf8Decode(S); - {$ENDIF} - end; - -{$ENDIF} - -{$IFNDEF DELPHI_12} -function CharInSet(C: Char; const CharSet: TCharSet): Boolean; -begin - Result := C in CharSet; -end; -{$ENDIF} - -constructor TContainer.Create; -begin - Inherited Create; - FItems := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; -end; - -Destructor TContainer.Destroy; -begin - FreeAll; - FItems.Free; - Inherited Destroy; -end; - -function TContainer.At(Index : integer) : Pointer; -begin - Try - Result := FItems[Index]; - Except - On E:EListError do Result := nil; - end; -end; - -procedure TContainer.AtDelete(Index : integer); -begin - FItems.Delete(Index); -end; - -procedure TContainer.AtInsert( Index : integer; Item : pointer ); -begin - FItems.Insert( Index, Item ); -end; - -procedure TContainer.AtPut( Index : integer; Item : pointer ); -begin - FItems[ Index ] := Item; -end; - -procedure TContainer.Clear; -begin - FItems.Clear; -end; - -procedure TContainer.Delete( Item : pointer ); -var - i : Integer; -begin - i := IndexOf( Item ); - if i <> -1 then AtDelete(i); -end; - -procedure TContainer.DeleteAll; -begin - FItems.Clear; -end; - -procedure TContainer.FreeAll; -var - I : integer; -begin - Try - for I := Count -1 downto 0 do - TObject(At(I)).Free; - Except - On EListError do ; - End; - FItems.Clear; -end; - -function TContainer.Get(AIndex : integer) : pointer; -begin - Result := FItems[AIndex]; -end; - -function TContainer.GetCount: integer; -begin - Result := FItems.Count; -end; - -function TContainer.IndexOf( Item : pointer ) : integer; -begin - Result := FItems.IndexOf( Item ); -end; - -procedure TContainer.Insert(Item : pointer); -begin - FItems.Add(Item); -end; - -procedure TContainer.Pack; -begin - FItems.Pack; -end; - -procedure TContainer.Put( AIndex : integer; APointer : pointer ); -begin - FItems[AIndex] := APointer; -end; - -function TContainer.GetCapacity : Integer; -begin - Result := FItems.Capacity; -end; - -procedure TContainer.SetCapacity( NewCapacity : Integer ); -begin - FItems.Capacity := NewCapacity; -end; - -///////////////////////////////////////////////////////////////////////////// -// IMPLEMENTATION TBASEOBJECT OBJECT // -///////////////////////////////////////////////////////////////////////////// -constructor TBaseObject.Create(P : TObject; Container : TContainer); -begin - Inherited Create; - FParent := P; - FContainer := Container; - if FContainer <> nil then FContainer.Insert(Self); -end; - -Destructor TBaseObject.Destroy; -begin - if FContainer <> nil then FContainer.Delete(Self); - Inherited Destroy; -end; - -///////////////////////////////////////////////////////////////////////////// -// IMPLEMENTATION COMMON FUNCTIONS // -///////////////////////////////////////////////////////////////////////////// -{ SQL Parser } -function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; -var - DotStart: Boolean; - - function NextTokenIs(Value: string; var Str: string): Boolean; - var - Tmp: PChar; - S: string; - begin - Tmp := p; - NextSQLToken(Tmp, S, CurSection); - Result := AnsiCompareText(Value, S) = 0; - if Result then - begin - Str := Str + ' ' + S; - p := Tmp; - end; - end; - - function GetSQLToken(var Str: string): TSQLToken; - var - l: PChar; - s: string; - begin - if Length(Str) = 0 then - Result := stEnd else - if (Str = '*') and (CurSection = stSelect) then - Result := stAllFields else - if DotStart then - Result := stFieldName else - if (AnsiCompareText('DISTINCT', Str) = 0) and (CurSection = stSelect) then - Result := stDistinct else - if (AnsiCompareText('ASC', Str) = 0) or (AnsiCompareText('ASCENDING', Str) = 0)then - Result := stAscending else - if (AnsiCompareText('DESC', Str) = 0) or (AnsiCompareText('DESCENDING', Str) = 0)then - Result := stDescending else - if (AnsiCompareText('SELECT', Str) = 0) and (CurSection = stUnknown) then - Result := stSelect else - if (AnsiCompareText('SELECT', Str) = 0) and (CurSection in [stSelect, stFieldName]) then - Result := stSubSelect else - if AnsiCompareText('AND', Str) = 0 then - Result := stAnd else - if AnsiCompareText('OR', Str) = 0 then - Result := stOr else - if AnsiCompareText('LIKE', Str) = 0 then - Result := stLike else - if (AnsiCompareText('AS', Str) = 0) then - Result := stAs else - if (AnsiCompareText('IS', Str) = 0) then - begin - if NextTokenIs('NULL', Str) then - Result := stIsNull else - begin - l := p; - s := Str; - if NextTokenIs('NOT', Str) and NextTokenIs('NULL', Str) then - Result := stIsNotNull else - begin - p := l; - Str := s; - Result := stValue; - end; - end; - end else - if AnsiCompareText('FROM', Str) = 0 then - Result := stFrom else - if AnsiCompareText('WHERE', Str) = 0 then - Result := stWhere else - if (AnsiCompareText('GROUP', Str) = 0) and NextTokenIs('BY', Str) then - Result := stGroupBy else - if AnsiCompareText('HAVING', Str) = 0 then - Result := stHaving else - if AnsiCompareText('UNION', Str) = 0 then - Result := stUnion else - if AnsiCompareText('PLAN', Str) = 0 then - Result := stPlan else - if (AnsiCompareText('FOR', Str) = 0) and NextTokenIs('UPDATE', Str) then - Result := stForUpdate else - if (AnsiCompareText('ORDER', Str) = 0) and NextTokenIs('BY', Str) then - Result := stOrderBy else - if AnsiCompareText('NULL', Str) = 0 then - Result := stValue else - if AnsiCompareText('SUBSTRING', Str) = 0 then - Result := stFunction else - if AnsiCompareText('TRIM', Str) = 0 then - Result := stFunction else - if CurSection = stFrom then - Result := stTableName else - if (CurSection = stTableName) or (CurSection = stAs) then - Result := stAliace else - Result := stFieldName; - - end; - -var - TokenStart: PChar; - - procedure StartToken; - begin - if not Assigned(TokenStart) then - TokenStart := p; - end; - -var - Literal: Char; - Mark: PChar; - BracketCount : integer; - LoopEnd: boolean; -begin - TokenStart := nil; - DotStart := False; - while True do - begin - case p^ of - '"','''','`': - begin - StartToken; - Literal := p^; - Mark := p; - //changed by pasha_golub 29.12.04, to deal with schema names - repeat - Inc(p); - LoopEnd := (p^ = Literal) or (p^ = #0); - if LoopEnd and (p^ <> #0) then - begin - inc(p); - if p^ = '.' then - begin - inc(p,2); - LoopEnd := False; - end; - end; - until LoopEnd; - if p^ = #0 then - begin - p := Mark; - Inc(p); - end else - begin - Inc(p); - SetString(Token, TokenStart, p - TokenStart); - Token := Trim(Token); - if DotStart then - Result := stFieldName else - if p^ = '.' then - Result := stTableName else - Result := stValue; - Exit; - end; - end; - '/': - begin - StartToken; - Inc(p); - if (p^ = '/') or (p^ = '*') then - begin - if p^ = '*' then - begin - repeat Inc(p) until (p = #0) or ((p^ = '*') and (p[1] = '/')); - end else - while (p^ <> #0) and (p^ <> #10) and (p^ <> #13) do Inc(p); - SetString(Token, TokenStart, p - TokenStart); - Result := stComment; - Exit; - end; - end; - ' ', #10, #13, ',', '(' ,')': - begin - if Assigned(TokenStart) then - begin - SetString(Token, TokenStart, p - TokenStart); - Result := GetSQLToken(Token); - if Result = stSubSelect then - repeat Inc(p) until (p^ = ')') else - Exit; - end else - begin - {$IFNDEF NEXTGEN} - if not CharInSet(p^, ['(',')']) then - while CharInSet(p^, [' ', #10, #13, ',']) do Inc(p) else - {$ELSE} - if not p^.IsInArray(['(',')']) then - while p^.IsInArray([' ', #10, #13, ',']) do Inc(p) else - {$ENDIF} - begin - BracketCount := 1; - repeat - Inc(p); - if p^ = '(' then Inc(BracketCount); - if p^ = ')' then Dec(BracketCount); - until (BracketCount = 0) or (p^ = #0) {safety measure}; - Inc(p); - end; - end; - - end; - '.': - begin - if Assigned(TokenStart) then - begin - SetString(Token, TokenStart, p - TokenStart); - Result := stTableName; - Exit; - end else - begin - DotStart := True; - Inc(p); - end; - end; - '=','<','>': - begin - if not Assigned(TokenStart) then - begin - TokenStart := p; - {$IFNDEF NEXTGEN} - while CharInSet(p^, ['=','<','>']) do Inc(p); - {$ELSE} - while p^.IsInArray(['=','<','>']) do Inc(p); - {$ENDIF} - SetString(Token, TokenStart, p - TokenStart); - Result := stPredicate; - Exit; - end; - Inc(p); - end; - '0'..'9': - begin - if not Assigned(TokenStart) then - begin - TokenStart := p; - {$IFNDEF NEXTGEN} - while CharInSet(p^, ['0'..'9','.']) do Inc(p); - {$ELSE} - while p^.IsInArray(['0', '1', '2','3','4','5','6','7','8','9','.']) do Inc(p); - {$ENDIF} - SetString(Token, TokenStart, p - TokenStart); - Result := stNumber; - Exit; - end else - Inc(p); - end; - #0: - begin - if Assigned(TokenStart) then - begin - SetString(Token, TokenStart, p - TokenStart); - Result := GetSQLToken(Token); - Exit; - end else - begin - Result := stEnd; - Token := ''; - Exit; - end; - end; - else - StartToken; - Inc(p); - end; - end; -end; - -function GetTable(const SQL: String; var Aliace : String): string; -var - Start: PChar; - Token: string; - SQLToken, CurSection: TSQLToken; -begin - Result := ''; - Start := PChar(SQL); - CurSection := stUnknown; - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then CurSection := SQLToken; - until SQLToken in [stEnd, stFrom]; - if SQLToken = stFrom then - begin - repeat - SQLToken := NextSQLToken(Start, Token, CurSection); - if SQLToken in SQLSections then - CurSection := SQLToken else - if (SQLToken = stTableName) or (SQLToken = stValue) then - begin - Result := Token; - Aliace := ''; - if Start[0] = '.' then - begin - CurSection := SQLToken; - SQLToken := NextSQLToken(Start, Token, CurSection); - if (SQLToken = stFieldName) or (SQLToken = stValue) - then Result := Result + '.' + Token; - end; - - while (Start[0] = ' ') and not (SQLToken in [stEnd]) do - begin - CurSection := SQLToken; - SQLToken := NextSqlToken(Start, Token, CurSection); - if SQLToken = stAliace then - Aliace := Token; - end; - Exit; - end; - until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]); - end; -end; - -function SqlDateToDateTime(Value: string; const IsTime: boolean): TDateTime; -var - Year, Month, Day, Hour, Min, Sec, MSec: Integer; - Temp: string; -begin - Temp := Value; - try - if not IsTime then - begin - Year := StrToIntDef(Copy(Temp,1,4),1); - Month := StrToIntDef(Copy(Temp,6,2),1); - Day := StrToIntDef(Copy(Temp,9,2),1); - Result := EncodeDate(Year, Month, Day); - end - else - begin - Hour := StrToIntDef(Copy(Temp,1,2),0); - Min := StrToIntDef(Copy(Temp,4,2),0); - Sec := StrToIntDef(Copy(Temp,7,2),0); - MSec := StrToIntDef(Copy(Copy(Temp,10,3) + '000',1,3),0); //19.05.2008: for cases when trailing 0 are missing - Result := EncodeTime(Hour, Min, Sec, Msec); - end; - except - Result := 0; - end; -end; - -function DateTimeToSqlDate(Value: TDateTime; Mode: Integer): string; -begin - Result := ''; - case Mode of - TIMESTAMP_MODE: Result := FormatDateTime('mm-dd-yyyy hh:nn:ss.zzz', Value, PSQL_FS); - DATE_MODE: Result := FormatDateTime('mm-dd-yyyy', Value, PSQL_FS); - TIME_MODE: Result := FormatDateTime('hh:nn:ss.zzz', Value, PSQL_FS); - end; -end; - -function SQLTimestampToDateTime(Value: string): TDateTime; -var - Year, Month, Day, Hour, Min, Sec, MSec, Idx: Integer; -{$IFDEF DELPHI_5} -const - MinDateTime: TDateTime = -657434.0; { 01/01/0100 12:00:00.000 AM } - MaxDateTime: TDateTime = 2958465.99999; { 12/31/9999 11:59:59.999 PM } -{$ENDIF} -begin - if value = 'infinity' then - Result := MaxDateTime //EncodeDate(9999, 12, 31) + EncodeTime(0, 0, 0, 0) - else - if value = '-infinity' then - Result := MinDateTime //EncodeDate(0, 1, 1) + EncodeTime(0, 0, 0, 0) - else - begin - for Idx := Length(Value) downto Length(Value) - TIMEZONELEN do //crop timezone information "+\-dd:dd" - {$IFNDEF NEXTGEN} - if CharInSet(Value[Idx], ['+', '-']) then - {$ELSE} - if Value[Idx].IsInArray(['+', '-']) then - {$ENDIF} - begin - Value := Copy(Value, 1, Idx - 1); - Break; - end; - Year := Max(1, StrToIntDef(Copy(Value, 1, 4), 1)); - Month := Max(1, StrToIntDef(Copy(Value, 6, 2), 1)); - Day := Max(1, StrToIntDef(Copy(Value, 9, 2), 1)); - Hour := StrToIntDef(Copy(Value, 12, 2), 0); - Min := StrToIntDef(Copy(Value, 15, 2), 0); - Sec := StrToIntDef(Copy(Value, 18, 2), 0); - Msec := StrToIntDef(Copy(Copy(Value, 21, 3) + '000', 1, 3), 0); //19.05.2008: for cases when trailing 0 are missing - Result := EncodeDate(Year, Month, Day); - if Result >= 0 then - Result := Result + EncodeTime(Hour, Min, Sec, MSec) - else - Result := Result - EncodeTime(Hour, Min, Sec, MSec); - end; -end; - -function StrToSQLFloat(Value: string): Double; -begin - if Value <> '' then - try - Result := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Value, PSQL_FS); - except - Result := 0; - end - else - Result := 0; -end; - -function SQLFloatToStr(Value: Double): string; -begin - Result := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}FloatToStr(Value, PSQL_FS); -end; - - -procedure GetToken(var Buffer, Token: string); -label ExitProc; -var - P: Integer; - Quote: string; -begin - P := {$IFNDEF NEXTGEN}1{$ELSE}0{$ENDIF}; - Token := ''; - if Buffer = '' then Exit; - {$IFNDEF NEXTGEN} - while CharInSet(Buffer[P], [' ',#9]) do - {$ELSE} - while Buffer[P].IsInArray([' ',#9]) do - {$ENDIF} - begin - Inc(P); - if Length(Buffer) < P then goto ExitProc; - end; - if (Pos(Buffer[P],DELIMITERS) <> 0) then - begin - Token := Buffer[P]; - Inc(P); - goto ExitProc; - end; - {$IFNDEF NEXTGEN} - if CharInSet(Buffer[P], ['"','''']) then - {$ELSE} - if Buffer[P].IsInArray(['"','''']) then - {$ENDIF} - begin - Quote := Buffer[P]; - Token := Quote; - Inc(P); - while P <= Length(Buffer) do - begin - Token := Token + Buffer[P]; - Inc(P); - if (Buffer[P-1] = Quote) and (Buffer[P-2] <> '\') then Break; - end; - end else - begin - while P <= Length(Buffer) do - begin - Token := Token + Buffer[P]; - Inc(P); - if (P > Length(Buffer)) or (Pos(Buffer[P],DELIMITERS) <> 0) - {$IFNDEF NEXTGEN} - or CharInSet(Buffer[P], ['"','''']) then Break; - {$ELSE} - or Buffer[P].IsInArray(['"','''']) then Break; - {$ENDIF} - end; - end; -ExitProc: - Delete(Buffer, 1, {$IFNDEF NEXTGEN}P-1{$ELSE}P{$ENDIF}); -end; - -function GetInArrayField(FieldType : Word): boolean; -var - I : Integer; -begin - Result := False; - for i := 0 to MAXARRFLDTYPES-1 do - begin - if FieldType = FldArrayType[I] then - begin - Result := True; - Break; - end; - end; -end; - -{$IFNDEF DELPHI_17} -function UIntToStr(C: cardinal): string; -begin - Result := IntToStr(C); -end; -{$ENDIF} - -function StrToUInt(S: string): cardinal; -var E: integer; -begin - Val(S, Result, E); - if E <> 0 then raise EConvertError.Create(S + ' is not valid cardinal value'); -end; - -function StrToUIntDef(S: string; DefVal: cardinal = 0): cardinal; -var R: int64; -begin - R := StrToInt64Def(S, DefVal); - if (R >= Low(Cardinal)) and (R <= High(Cardinal)) then - Result := R - else - Result := DefVal; -end; - -function DeBracketedStr(const Value: string; ALeftBracket, ARightBracket: char): string; -var I, L: integer; -begin - L := length(Value); - for I := 1 to L div 2 do - if (Value[i] <> ALeftBracket) OR (Value[L - i + 1] <> ARightBracket) then - Break; - Result := Copy(Value, I, L - 2 * (I - 1)); -end; - -procedure FieldMapping(FieldType : cardinal; phSize : Integer; var BdeType : integer; - var BdeSubType : integer; var LogSize : Integer; - var LocArray : Boolean); -begin - BdeType := fldUNKNOWN; - BdeSubType := 0; - LogSize := 0; - LocArray := GetInArrayField(FieldType); - Case FieldType of - FIELD_TYPE_BOOL: begin - BdeType := fldBOOL; - LogSize := SizeOf(SmallInt); - end; - FIELD_TYPE_BPCHAR, - FIELD_TYPE_CHAR, - FIELD_TYPE_VARCHAR, - FIELD_TYPE_TINTERVAL: - begin - BdeType := fldZSTRING; - LogSize := phSize + 1; - if FieldType = FIELD_TYPE_BPCHAR then - BdeSubType := fldstFIXED; - end; - FIELD_TYPE_OID, - FIELD_TYPE_BYTEA: begin - BdeType := fldBLOB; - LogSize := SizeOf(TBlobItem); - end; - FIELD_TYPE_TEXT: begin - BdeType := fldBLOB; - LogSize := SizeOf(TBlobItem); - BdeSubType := fldstMemo; - end; - FIELD_TYPE_INT2: begin - BDEType := fldINT16; - LogSize := Sizeof(SmallInt); - end; - FIELD_TYPE_INT4: begin - BDEType := fldINT32; - LogSize := Sizeof(LongInt); - end; - FIELD_TYPE_INT8: begin - BDEType := fldINT64; - LogSize := Sizeof(Int64); - end; - FIELD_TYPE_DATE: begin - BdeType := fldDATE; - LogSize := Sizeof(TTimeStamp); - end; - FIELD_TYPE_TIME: begin - BdeType := fldTIME; - LogSize := Sizeof(TDateTime); - end; - - FIELD_TYPE_TIMESTAMP: - begin - BdeType := fldTIMESTAMP; - LogSize := SizeOf(TDateTime); - end; - FIELD_TYPE_TIMESTAMPTZ: begin - BdeType := fldZSTRING; - LogSize := TIMESTAMPTZLEN + 1; - end; -{$IFDEF UNDER_DELPHI_12} - FIELD_TYPE_NUMERIC, -{$ENDIF} - FIELD_TYPE_FLOAT4, - FIELD_TYPE_FLOAT8: - begin - BdeType := fldFLOAT; - LogSize := Sizeof(Double); - end; -{$IFDEF DELPHI_12} - FIELD_TYPE_NUMERIC: begin - BdeType := fldFMTBCD; - LogSize := phSize; - end; -{$ENDIF} - FIELD_TYPE_MONEY: begin - BdeType := fldZSTRING; - //BdeSubType := fldstMONEY; - LogSize := 32; //Sizeof(Single); - end; - FIELD_TYPE_NAME: begin - BdeType := fldZSTRING; - LogSize := NAMEDATALEN + 1; - end; - FIELD_TYPE_TIMETZ: begin - BdeType := fldZSTRING; - LogSize := TIMETZLEN + 1; - end; - FIELD_TYPE_BIT: begin - BdeType := fldZSTRING; - LogSize := phSize + 1; - end; - FIELD_TYPE_UUID: begin - BdeType := fldUUID; - LogSize := UUIDLEN + 1; - end; - FIELD_TYPE_INET, - FIELD_TYPE_CIDR: begin - BdeType := fldZSTRING; - LogSize := INETLEN + 1; - end; - FIELD_TYPE_MACADDR: begin - BdeType := fldZSTRING; - LogSize := MACADDRLEN + 1; - end; -{$IFDEF DELPHI_12} - FIELD_TYPE_POINT: - begin - BdeType := fldPOINT; - LogSize := SizeOf(TPSQLPoint); - end; - FIELD_TYPE_CIRCLE: - begin - BdeType := fldCIRCLE; - LogSize := SizeOf(TPSQLCircle); - end; - FIELD_TYPE_BOX: - begin - BdeType := fldBOX; - LogSize := SizeOf(TPSQLBox); - end; - FIELD_TYPE_LSEG: - begin - BdeType := fldLSEG; - LogSize := SizeOf(TPSQLLSeg); - end; - FIELD_TYPE_NUMRANGE, - FIELD_TYPE_DATERANGE, - FIELD_TYPE_INT4RANGE, - FIELD_TYPE_INT8RANGE, - FIELD_TYPE_TSRANGE, - FIELD_TYPE_TSTZRANGE: - begin - BdeType := fldRANGE; - LogSize := SizeOf(TPSQLRange); - end -{$ENDIF DELPHI_12} - else - begin - BdeType := fldZSTRING; - LogSize := phSize+1; - end; - end; -end; - -procedure ConverPSQLtoDelphiFieldInfo(Info : TPGFIELD_INFO; - Count, Offset : integer; - var RecBuff : FLDDesc; - var ValChk : VCHKDesc; - var LocArray : Boolean); -var - LogSize : Integer; - dataLen : Integer; -begin - {$IFNDEF NEXTGEN} - ZeroMemory(@RecBuff, Sizeof(FLDDesc)); - ZeroMemory(@ValChk, SizeOf(VCHKDesc)); - {$ELSE} - FillChar(RecBuff, Sizeof(FLDDesc), 0); - FillChar(ValChk, SizeOf(VCHKDesc), 0); - {$ENDIF} - with RecBuff do - begin - iFldNum := Count; - iNativeType := Info.FieldType; - ValChk.iFldNum := Count; - DataLen := Info.FieldMaxSize; - FieldMapping(Info.FieldType, DataLen, iFldType, iSubType, LogSize, LocArray); - iUnits2 := 0; - case Info.Fieldtype of - FIELD_TYPE_NUMERIC: - if Info.FieldTypMod > 0 then - begin - iUnits1 := (Info.FieldTypMod - 4) shr 16 and 65535; - iUnits2 := (Info.FieldTypMod - 4) and 65535; - end else - begin - iUnits1 := NUMERIC_PREC; - iUnits2 := NUMERIC_SCALE; - end; - else - iUnits1 := ifthen(iFldType = fldZSTRING, LogSize - 1, LogSize); - end; - iLen := LogSize; - if (iFldType = fldINT32) and (Pos('nextval(', Info.FieldDefault) > 0) then iSubType := fldstAUTOINC; - iOffset := Offset; - efldvVchk := fldvUNKNOWN; - ValChk.bHasDefVal := Info.FieldDefault <> ''; - ValChk.aDefVal := Info.FieldDefault; - ValChk.bRequired := Info.FieldNotNull; - szName := Info.FieldName; - end; -end; - -procedure LoadPSQLLibrary(LibPQPath: string = ''); - - function GetPSQLProc( ProcName : string ) : pointer; - begin - Result := GetProcAddress( SQLLibraryHandle, PChar(ProcName)); - {$IFDEF M_DEBUG} - if not Assigned(Result) then - LogDebugMessage('PROC', Format('No entry address for procedure "%s"', [ProcName])); - {$ENDIF} - - end; - -begin - if LibPQPath = EmptyStr then LibPQPath := PSQL_DLL; - {$IFDEF M_DEBUG} - LogDebugMessage('LIB', 'Trying load ' + LibPQPath); - {$ENDIF} - if ( SQLLibraryHandle <= HINSTANCE_ERROR ) then - begin - // we must think about some property - {$IFDEF ANDROID} - //internal path - LibPQPath := TPath.Combine(TPath.GetDocumentsPath, LibPQPath); - //external path - // LibPQPath := TPath.Combine(TPath.GetSharedDocumentsPath, LibPQPath); - {$ENDIF} - - SQLLibraryHandle := LoadLibrary(PChar(LibPQPath)); - {$IFDEF M_DEBUG} - LogDebugMessage('LIB', 'Handle for module: ' + IntToStr(SQLLibraryHandle)); - {$ENDIF} - if ( SQLLibraryHandle > HINSTANCE_ERROR ) then - begin - @PQlibVersion := GetPSQLProc('PQlibVersion'); - @PQisthreadsafe := GetPSQLProc('PQisthreadsafe'); - @PQconnectdb := GetPSQLProc('PQconnectdb'); - @PQconnectdbParams := GetPSQLProc('PQconnectdbParams'); - @PQping := GetPSQLProc('PQping'); - @PQpingParams := GetPSQLProc('PQpingParams'); - @PQconnectPoll := GetPSQLProc('PQconnectPoll'); - @PQconnectStart := GetPSQLProc('PQconnectStart'); - @PQsetdbLogin := GetPSQLProc('PQsetdbLogin'); - @PQconndefaults := GetPSQLProc('PQconndefaults'); - @PQfinish := GetPSQLProc('PQfinish'); - @PQreset := GetPSQLProc('PQreset'); - @PQrequestCancel := GetPSQLProc('PQrequestCancel'); - @PQdb := GetPSQLProc('PQdb'); - @PQuser := GetPSQLProc('PQuser'); - @PQpass := GetPSQLProc('PQpass'); - @PQhost := GetPSQLProc('PQhost'); - @PQport := GetPSQLProc('PQport'); - @PQtty := GetPSQLProc('PQtty'); - @PQoptions := GetPSQLProc('PQoptions'); - @PQstatus := GetPSQLProc('PQstatus'); - @PQerrorMessage := GetPSQLProc('PQerrorMessage'); - @PQsocket := GetPSQLProc('PQsocket'); - @PQbackendPID := GetPSQLProc('PQbackendPID'); - @PQparameterStatus := GetPSQLProc('PQparameterStatus'); - @PQserverVersion:= GetPSQLProc('PQserverVersion'); - @PQtrace := GetPSQLProc('PQtrace'); - @PQuntrace := GetPSQLProc('PQuntrace'); - @PQsetNoticeProcessor := GetPSQLProc('PQsetNoticeProcessor'); - @PQexecPrepared := GetPSQLProc('PQexecPrepared'); - @PQprepare := GetPSQLProc('PQprepare'); - @PQexec := GetPSQLProc('PQexec'); - @PQsetSingleRowMode := GetPSQLProc('PQsetSingleRowMode'); - @PQexecParams := GetPSQLProc('PQexecParams'); - @PQnotifies := GetPSQLProc('PQnotifies'); - @PQsendQuery := GetPSQLProc('PQsendQuery'); - @PQgetResult := GetPSQLProc('PQgetResult'); - @PQisBusy := GetPSQLProc('PQisBusy'); - @PQconsumeInput := GetPSQLProc('PQconsumeInput'); - @PQgetline := GetPSQLProc('PQgetline'); - @PQputline := GetPSQLProc('PQputline'); - @PQgetlineAsync := GetPSQLProc('PQgetlineAsync'); - @PQputnbytes := GetPSQLProc('PQputnbytes'); - @PQendcopy := GetPSQLProc('PQendcopy'); - @PQgetCopyData := GetPSQLProc('PQgetCopyData'); - @PQputCopyData := GetPSQLProc('PQputCopyData'); - @PQputCopyEnd := GetPSQLProc('PQputCopyEnd'); - @PQresultStatus := GetPSQLProc('PQresultStatus'); - @PQresultErrorMessage := GetPSQLProc('PQresultErrorMessage'); - @PQresultErrorField := GetPSQLProc('PQresultErrorField'); - @PQntuples := GetPSQLProc('PQntuples'); - @PQnfields := GetPSQLProc('PQnfields'); - @PQbinaryTuples := GetPSQLProc('PQbinaryTuples'); - @PQfname := GetPSQLProc('PQfname'); - @PQfnumber := GetPSQLProc('PQfnumber'); - @PQftype := GetPSQLProc('PQftype'); - @PQfformat := GetPSQLProc('PQfformat'); - @PQftable := GetPSQLProc('PQftable'); - @PQftablecol := GetPSQLProc('PQftablecol'); - @PQfsize := GetPSQLProc('PQfsize'); - @PQfmod := GetPSQLProc('PQfmod'); - @PQcmdStatus := GetPSQLProc('PQcmdStatus'); - @PQoidValue := GetPSQLProc('PQoidValue'); - @PQoidStatus := GetPSQLProc('PQoidStatus'); - @PQcmdTuples := GetPSQLProc('PQcmdTuples'); - @PQgetvalue := GetPSQLProc('PQgetvalue'); - @PQsetvalue := GetPSQLProc('PQsetvalue'); - @PQcopyResult := GetPSQLProc('PQcopyResult'); - @PQgetlength := GetPSQLProc('PQgetlength'); - @PQgetisnull := GetPSQLProc('PQgetisnull'); - @PQclear := GetPSQLProc('PQclear'); - @PQmakeEmptyPGresult := GetPSQLProc('PQmakeEmptyPGresult'); - @PQtransactionStatus := GetPSQLProc('PQtransactionStatus'); - @PQEscapeByteaConn := GetPSQLProc('PQescapeByteaConn'); - @PQUnEscapeBytea:= GetPSQLProc('PQunescapeBytea'); - @PQEscapeStringConn:=GetPSQLProc('PQescapeStringConn'); - @PQFreeMem := GetPSQLProc('PQfreemem'); - @PQsetClientEncoding := GetPSQLProc('PQsetClientEncoding'); - @PQsetErrorVerbosity := GetPSQLProc('PQsetErrorVerbosity'); - @PQclientEncoding := GetPSQLProc('PQclientEncoding'); - @PQgetssl := GetPSQLProc('PQgetssl'); - @pg_encoding_to_char := GetPSQLProc('pg_encoding_to_char'); - @lo_open := GetPSQLProc('lo_open'); - @lo_close := GetPSQLProc('lo_close'); - @lo_read := GetPSQLProc('lo_read'); - @lo_write := GetPSQLProc('lo_write'); - @lo_lseek := GetPSQLProc('lo_lseek'); - @lo_creat := GetPSQLProc('lo_creat'); - @lo_tell := GetPSQLProc('lo_tell'); - @lo_unlink := GetPSQLProc('lo_unlink'); - @lo_import := GetPSQLProc('lo_import'); - @lo_export := GetPSQLProc('lo_export'); - end - else - CheckLibraryLoaded(); - {$IFDEF M_DEBUG} - LogDebugMessage('LIB', GetModuleName(GetModuleHandle(PChar(LIBEAY_DLL)))); - LogDebugMessage('LIB', GetModuleName(GetModuleHandle(PChar(SSLEAY_DLL)))); - LogDebugMessage('LIB', GetModuleName(SQLLibraryHandle)); - {$ENDIF} - end; -end; - -procedure UnloadPSQLLibrary; -begin - if IsLibraryLoaded() then - FreeLibrary( SQLLibraryHandle ); - SQLLibraryHandle := HINSTANCE_ERROR; -end; - -procedure HotLibrarySwitch(const ALibPath: string); -var I: integer; -begin - for I := 0 to DBList.Count - 1 do - TPSQLDatabase(DBList[i]).Close; - UnloadPSQLLibrary; - LoadPSQLLibrary(ALibPath); -end; - -procedure CheckLibraryLoaded; -begin - if not IsLibraryLoaded() then - {$IFDEF DELPHI_5} - RaiseLastWin32Error; - {$ELSE} - RaiseLastOSError; - {$ENDIF} -end; - -function IsLibraryLoaded: boolean; -begin - Result := SQLLibraryHandle > HINSTANCE_ERROR; -end; - -function MaskSearch(const Str, Mask: string; - CaseSensitive : boolean = true; - MaskChar: Char = '?'; - WildCard: Char = '%'): Boolean;//mi:2006-09-07 -var - S, M : PChar; - W : PChar; //mi:2007-06-20 last wildcard position in mask -begin - Result := false; - if CaseSensitive then - begin - S := PChar(Str); - M := PChar(Mask); - end - else - begin - S := PChar(AnsiUpperCase(Str)); - M := PChar(AnsiUpperCase(Mask)); - end; - - W := nil; - - while true do - begin - if (S^ = #0) or (M^ = #0) then//we have an end of one of strings - begin - //mi:2007-10-14 there can be some more wildcard chars in mask - while (M^ = WildCard) do - inc(M); - - if S^ = M^ then //both are #0, it seems that we have a match or both strings are empty - Result := true; - exit; - end; - - if (M^ <> MaskChar) and (M^ <> WildCard) then - begin - if M^ = S^ then - begin - Inc(M); Inc(S); //move to the next character - continue; - end - else//character are not equal - begin - if W <> nil then//there was a wildcard before, we need to rollback mask to it to continue search - begin - M := W; - Inc(S); - continue; - end - else //there were no wildcards before, string doesn't match - exit; - end; - end - else if (M^ = MaskChar) then - begin - Inc(M); Inc(S); //move to the next character - continue; - end - else if (M^ = WildCard) then - begin - W := M; - while (S^ <> (M+1)^) and (S^ <> #0) do - Inc(S); - Inc(M); - end; - end; -end; - -function Search(Op1,Op2 : Variant; OEM, CaseSen : Boolean; PartLen: Integer):Boolean; -var - S1,S2 : String; -begin - if CaseSen then //case insensitive - begin - Op1 := AnsiUpperCase(Op1); - Op2 := AnsiUpperCase(Op2); - end; - S1 := Op1; - S2 := Op2; - -{$IFDEF MSWINDOWS} - if OEM then - begin - {$IFDEF DELPHI_12} - OemToCharBuff(PAnsiChar(AnsiString(S1)), PWideChar(S1), Length(S1)); - OemToCharBuff(PAnsiChar(AnsiString(S2)), PWideChar(S2), Length(S2)); - {$ELSE} - {$IFNDEF FPC} - OemToCharBuff(PAnsiChar(S1), PAnsiChar(S1), Length(S1)); - OemToCharBuff(PAnsiChar(S2), PAnsiChar(S2), Length(S2)); - {$ENDIF} - {$ENDIF} - end; -{$ENDIF} - - if CaseSen then //case insensitive - begin - if PartLen = 0 then - Result := AnsiStrIComp(PChar(S1), PChar(S2)) = 0 else // Full len - Result := AnsiStrLIComp(PChar(S1), PChar(S2), PartLen) = 0; //Part len - end else - begin - if PartLen = 0 then - Result := AnsiStrComp(PChar(S1), PChar(S2)) = 0 else // Full len - Result := AnsiStrLComp(PChar(S1), PChar(S2), PartLen) = 0; //Part len - end; -end; - -function GetBDEErrorMessage(ErrorCode : Word):String; -begin - case ErrorCode of - DBIERR_BOF: Result :='At beginning of table.'; //8705 - DBIERR_EOF: Result :='At end of table.'; //8706 - DBIERR_NOCURRREC: Result :='No current record.'; //8709 - DBIERR_RECNOTFOUND: Result :='Could not find record.'; //8710 - DBIERR_ENDOFBLOB: Result :='End of BLOB.'; //8711 - DBIERR_INVALIDPARAM: Result :='Invalid parameter.'; //9986 - DBIERR_INVALIDHNDL: Result :='Invalid handle to the function.'; //9990 - DBIERR_NOSUCHINDEX: Result :='Index does not exist.'; //9997 - DBIERR_INVALIDBLOBOFFSET: Result :='Invalid offset into the BLOB.'; //9998 - DBIERR_INVALIDRECSTRUCT: Result :='Invalid record structure.'; //10003 - DBIERR_NOSUCHTABLE: Result :='Table does not exist.'; //10024 - DBIERR_NOSUCHFILTER: Result :='Filter handle is invalid.'; //10050 - DBIERR_NOTSUFFTABLERIGHTS: Result :='Insufficient table rights for operation. Password required.';//10498 - DBIERR_NOTABLOB: Result :='Field is not a BLOB.'; //10753 - DBIERR_TABLEREADONLY: Result :='Table is read only.'; //10763 - DBIERR_NOASSOCINDEX: Result :='No associated index.'; //10764 - DBIERR_QRYEMPTY: Result :='Query string is empty.'; //11886 - DBIERR_NOTSUPPORTED: Result :='Capability not supported.'; //12289 - DBIERR_UPDATEABORT: Result :='Update aborted.'; //13062 - end; -end; - - -function DACAnsiStrAlloc(Size: Cardinal): PAnsiDACChar; -begin -{$IFNDEF MOBILE} - {$IFDEF DELPHI_12} - Result := AnsiStrAlloc(Size); - {$ELSE} - Result := StrAlloc(Size); - {$ENDIF} -{$ELSE} - Inc(Size, SizeOf(Cardinal)); - GetMem(Result, Size); - Cardinal(Pointer(Result)^) := Size; - Inc(Result, SizeOf(Cardinal)); -{$ENDIF} -end; - -procedure DACAnsiStrDispose(Str: PAnsiDACChar); -begin -{$IFNDEF NEXTGEN} -{$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}strdispose(Str); -{$ELSE} -if Str <> nil then - begin - Dec(Str, SizeOf(Cardinal)); - FreeMem(Str, Cardinal(Pointer(Str)^)); - end; -{$ENDIF} -end; - -function DACStrCopy(Dest: PAnsiDACChar; const Source: PAnsiDACChar; MaxLen: Cardinal = 0): PAnsiDACChar; -{$IFDEF NEXTGEN} -var - Len: Cardinal; -{$ENDIF} -begin - if MaxLen = 0 then - MaxLen := Length(Source); - - Result := Dest; - {$IFNDEF NEXTGEN} - {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrLCopy(Dest, Source, MaxLen); - {$ELSE} - Len := Length(Source); - if Len > MaxLen then - Len := MaxLen; - Move(Source^, Dest^, Len); - Dest[Len] := #0; - {$ENDIF} -end; - -procedure DACAllocStr(var Dest: PAnsiDACChar; Len: Integer); -//{$IFDEF NEXTGEN} -//var -// i: integer; -//{$ENDIF} -begin -{$IFNDEF NEXTGEN} - {$IFDEF DELPHI_12} - Dest := AnsiStrAlloc(Len + 1); - {$ELSE} - Dest := StrAlloc(Len + 1); - {$ENDIF} -{$ELSE} -// i := Length(String(Source)); - GetMem(Dest, Len+1); - FillChar(Dest^, Len+1, 0); -{$ENDIF} -end; - - -{$IFDEF MOBILE} -// Working with PAnsiChar for Mobile platform - -function DACAnsiStrBufSize(const Str: PAnsiDACChar): Cardinal; -var - P: PAnsiDACChar; -begin - P := Str; - Dec(P, SizeOf(Cardinal)); - Result := Cardinal(Pointer(P)^) - SizeOf(Cardinal); -end; - -function DACStrBufSize(const Str: PAnsiDACChar): Cardinal; -var - P: PAnsiDACChar; -begin - P := Str; - Dec(P, SizeOf(Cardinal)); - Result := Cardinal(Pointer(P)^) - SizeOf(Cardinal); -end; -{$ENDIF} - -{$IFNDEF MACOS_OR_MOBILE} -function GetTickCount: LongWord; -{$IFDEF DELPHI_12}inline;{$ENDIF} -begin - Result := Windows.GetTickCount; -end; -{$ELSE} -function GetTickCount: LongWord;inline; -begin -{$IFNDEF DELPHI_17} - Result := AbsoluteToNanoseconds(UpTime) div 1000000; -{$ELSE} - Result := TThread.GetTickCount; -{$ENDIF} -end; -{$ENDIF;} - - -initialization - SQLLibraryHandle := HINSTANCE_ERROR; - -finalization - UnloadPSQLLibrary; - -end. +{$I pSQLDAC.inc} +unit PSQLTypes; + +{SVN revision: $Id$} + +{$Z+,T-} //taken from MySQLDAC +interface + +uses {$IFDEF FPC}LCLIntf,{$ENDIF} + Classes, SysUtils, Math + + {$IFNDEF NEXTGEN} + {$IFDEF DELPHI_12}, AnsiStrings{$ENDIF} + {$ENDIF} + + {$IFDEF DELPHI_6}, FmtBcd{$ENDIF} + {$IFDEF DELPHI_12}, SqlTimSt, PSQLGeomTypes{$ENDIF} + {$IFDEF MSWINDOWS}, Windows{$ENDIF} + {$IFDEF ANDROID}, System.IOUtils {$ENDIF} + {$IFDEF MACOS}, Macapi.CoreServices{$ENDIF} + {$IFDEF NEXTGEN}, Generics.Collections{$ENDIF}; +{$IFDEF DELPHI_12} + {$NOINCLUDE PSQLGeomTypes} +{$ENDIF} + + +//============================================================================// +// Result Error Field Codes // +//============================================================================// +const + PG_DIAG_SEVERITY = ord('S'); + PG_DIAG_SQLSTATE = ord('C'); + PG_DIAG_MESSAGE_PRIMARY = ord('M'); + PG_DIAG_MESSAGE_DETAIL = ord('D'); + PG_DIAG_MESSAGE_HINT = ord('H'); + PG_DIAG_STATEMENT_POSITION = ord('P'); + PG_DIAG_INTERNAL_POSITION = ord('p'); + PG_DIAG_INTERNAL_QUERY = ord('q'); + PG_DIAG_CONTEXT = ord('W'); + PG_DIAG_SCHEMA_NAME = ord('s'); + PG_DIAG_TABLE_NAME = ord('t'); + PG_DIAG_COLUMN_NAME = ord('c'); + PG_DIAG_DATATYPE_NAME = ord('d'); + PG_DIAG_CONSTRAINT_NAME = ord('n'); + PG_DIAG_SOURCE_FILE = ord('F'); + PG_DIAG_SOURCE_LINE = ord('L'); + PG_DIAG_SOURCE_FUNCTION = ord('R'); + + +//============================================================================// +// Option flags for PQcopyResult // +//============================================================================// +const + PG_COPYRES_ATTRS = 01; + PG_COPYRES_TUPLES = 02; // Implies PG_COPYRES_ATTRS + PG_COPYRES_EVENTS = 04; + PG_COPYRES_NOTICEHOOKS = 08; + +//============================================================================// +// Error Categories // +//============================================================================// +const + ERRBASE_NONE = 0; { No error } + ERRBASE_NOTFOUND = $2200; { Object of interest Not Found } + ERRBASE_INVALIDREQ = $2700; { Invalid Request } + ERRBASE_SEC = $2900; { Access Violation - Security related } + ERRBASE_IC = $2A00; { Invalid context } + ERRBASE_QUERY = $2E00; { Query related } + ERRBASE_CAPABILITY = $3000; { Capability not supported } + ERRBASE_OTHER = $3300; { Miscellaneous } +//=============================================================================// +// Error Codes By Category // +//=============================================================================// + ERRCODE_NONE = 0; + DBIERR_NONE = (ERRBASE_NONE + ERRCODE_NONE); + ERRCODE_BOF = 1; { Beginning of Virtual table } + ERRCODE_EOF = 2; { End of Virtual table } + ERRCODE_NOCURRREC = 5; { No current record } + ERRCODE_RECNOTFOUND = 6; { Record was not found } + ERRCODE_ENDOFBLOB = 7; { End of Blob reached } + DBIERR_BOF = (ERRBASE_NOTFOUND + ERRCODE_BOF); + DBIERR_EOF = (ERRBASE_NOTFOUND + ERRCODE_EOF); + DBIERR_NOCURRREC = (ERRBASE_NOTFOUND + ERRCODE_NOCURRREC); + DBIERR_RECNOTFOUND = (ERRBASE_NOTFOUND + ERRCODE_RECNOTFOUND); + DBIERR_ENDOFBLOB = (ERRBASE_NOTFOUND + ERRCODE_ENDOFBLOB); + ERRCODE_INVALIDPARAM = 2; { Generic invalid parameter } + ERRCODE_INVALIDHNDL = 6; { Invalid handle to the function } + ERRCODE_NOSUCHINDEX = 13; { 0x0d Index does not exist } + ERRCODE_INVALIDBLOBOFFSET = 14; { 0x0e Invalid Offset into the Blob } + ERRCODE_INVALIDRECSTRUCT = 19; { 0x13 Invalid record structure } + ERRCODE_NOSUCHTABLE = 40; { 0x28 No such table } + ERRCODE_NOSUCHFILTER = 66; { 0x42 Filter handle is invalid } + DBIERR_INVALIDPARAM = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDPARAM); + DBIERR_INVALIDHNDL = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDHNDL); + DBIERR_NOSUCHINDEX = (ERRBASE_INVALIDREQ + ERRCODE_NOSUCHINDEX); + DBIERR_INVALIDBLOBOFFSET = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDBLOBOFFSET); + DBIERR_INVALIDRECSTRUCT = (ERRBASE_INVALIDREQ + ERRCODE_INVALIDRECSTRUCT); + DBIERR_NOSUCHTABLE = (ERRBASE_INVALIDREQ + ERRCODE_NOSUCHTABLE); + DBIERR_NOSUCHFILTER = (ERRBASE_INVALIDREQ + ERRCODE_NOSUCHFILTER); +{ ERRCAT_SECURITY } +{ =============== } + ERRCODE_NOTSUFFTABLERIGHTS = 2; { Not sufficient table rights for operation } + DBIERR_NOTSUFFTABLERIGHTS = (ERRBASE_SEC + ERRCODE_NOTSUFFTABLERIGHTS); +{ ERRCAT_INVALIDCONTEXT } +{ ===================== } + ERRCODE_NOTABLOB = 1; { Field is not a blob } + ERRCODE_TABLEREADONLY = 11; { 0x0b Table is read only } + ERRCODE_NOASSOCINDEX = 12; { 0x0c No index associated with the cursor } + DBIERR_NOTABLOB = (ERRBASE_IC + ERRCODE_NOTABLOB); + DBIERR_TABLEREADONLY = (ERRBASE_IC + ERRCODE_TABLEREADONLY); + DBIERR_NOASSOCINDEX = (ERRBASE_IC + ERRCODE_NOASSOCINDEX); +{ ERRCAT_NETWORK } +{ ERRCAT_QUERY } +{ ============ } + DBICODE_QRYEMPTY = 110; { 0x6e } + DBIERR_QRYEMPTY = (ERRBASE_QUERY+ DBICODE_QRYEMPTY); +{ END_OF_QUERY_MESSAGES } + +{ ERRCAT_CAPABILITY } +{ ================= } + ERRCODE_NOTSUPPORTED = 1; { Capability not supported } + DBIERR_NOTSUPPORTED = (ERRBASE_CAPABILITY + ERRCODE_NOTSUPPORTED); +{ ERRCAT_OTHER } +{ ============ } + ERRCODE_UPDATEABORT = 6; { Update operation aborted } + DBIERR_UPDATEABORT = (ERRBASE_OTHER + ERRCODE_UPDATEABORT); + +///////////////////////////////////////////////////////////////////////////// +// COMPATIBILITY TYPES // +///////////////////////////////////////////////////////////////////////////// +type + PAnsiDACChar = {$IFDEF NEXTGEN}MarshaledAString{$ELSE}PAnsiChar{$ENDIF}; + PAnsiDACBytesChar = {$IFDEF NEXTGEN}PByte{$ELSE}PAnsiChar{$ENDIF}; + AnsiDACChar = {$IFDEF NEXTGEN}Char{$ELSE}AnsiChar{$ENDIF}; + DACAString = {$IFDEF NEXTGEN}String{$ELSE}AnsiString{$ENDIF}; + DACABytesString = {$IFDEF NEXTGEN}TBytes{$ELSE}AnsiString{$ENDIF}; + AnsiDACByteChar = {$IFDEF NEXTGEN}Byte{$ELSE}AnsiChar{$ENDIF}; +{$IFDEF DELPHI_16} + DACPointerInt = NativeInt; +{$ELSE} + DACPointerInt = integer; +{$ENDIF} + +const + START_STR_INDEX = {$IFDEF ZEROBASEDSTRINGS}0{$ELSE}1{$ENDIF}; + +{$IFNDEF DELPHI_12} + type + NativeUInt = cardinal; +{$ENDIF} + +{$IFDEF UNDER_DELPHI_6} + type + PBoolean = ^Boolean; + PWordBool = ^WordBool; + + TFormatSettings = record + CurrencyFormat: Byte; + NegCurrFormat: Byte; + ThousandSeparator: Char; + DecimalSeparator: Char; + CurrencyDecimals: Byte; + DateSeparator: Char; + TimeSeparator: Char; + ListSeparator: Char; + CurrencyString: string; + ShortDateFormat: string; + LongDateFormat: string; + TimeAMString: string; + TimePMString: string; + ShortTimeFormat: string; + LongTimeFormat: string; + ShortMonthNames: array[1..12] of string; + LongMonthNames: array[1..12] of string; + ShortDayNames: array[1..7] of string; + LongDayNames: array[1..7] of string; + TwoDigitYearCenturyWindow: Word; + end; +{$ENDIF} + +const + NAMEDATALEN = 64; + DATELEN = length('2001-02-17'); + TIMEZONELEN = length('+10:30'); + TIMESTAMPTZLEN = length('2001-02-17 07:08:40.123456+10:30'); + TIMETZLEN = length('13:45:35.4880123457+13:40'); + UUIDLEN = length('{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}'); + INETLEN = length('7628:0d18:11a3:09d7:1f34:8a2e:07a0:765d/128'); + MACADDRLEN = length('08:00:2b:01:02:03'); + OIDNAMELEN = 36; + INV_WRITE = $00020000; + INV_READ = $00040000; + PG_SEEK_SET = 0; // Seek from beginning of file + PG_SEEK_CUR = 1; // Seek from current position + PG_SEEK_END = 2; // Seek from end of file + DELIMITERS : string = ' .:;,+-<>/*%^=()[]|&~@#$\`{}!?'#10#13; + PSQL_PORT = 5432; + MINLONGINT = -MaxLongInt; + MAX_BLOB_SIZE = 8192; //Max Blob size for read and write operation + MAX_CHAR_LEN = 8192; //Max character length allowed in TField descendants + MAX_ENCODING_ID = 42; //Max encoding id for pg_encoding_to_char + InvalidOid : cardinal = 0; + NUMERIC_PREC = 32; //Default precision for TFmtBcdField if not specified for NUMERIC + NUMERIC_SCALE = 6; //Default scale for TFmtBcdField if not specified for NUMERIC + + +const //date/time convertion + TIMESTAMP_MODE = 0; + DATE_MODE = 1; + TIME_MODE = 2; + +{$IFDEF FPC} + const + HINSTANCE_ERROR = 32; +{$ELSE} + {$IFNDEF MSWINDOWS} + const + HINSTANCE_ERROR = 32; + {$ENDIF} +{$ENDIF} + +const + LIBEAY_DLL : string = 'libcrypto-1_1.dll'; + SSLEAY_DLL : string = 'libssl-1_1.dll'; + +var + PSQL_DLL : string = + {$IFDEF MSWINDOWS}'libpq.dll'{$ENDIF} + {$IFDEF MACOS}'libpq.dylib'{$ENDIF} + {$IFDEF ANDROID}'libpq.so'{$ENDIF} + {$IFDEF LINUX}'libpq.so'{$ENDIF}; + PSQL_DLL_WITHOUT_SLL : string = 'libpq-without-SSL.dll'; + + + SQLLibraryHandle : THandle = HINSTANCE_ERROR; + OEMConv : Boolean; //Global OEM->ANSI Variable + PSQL_FS : TFormatSettings; + +type + + + TPSQLDACAbout = class + end; + + TPSQLDatasetSortCompare = function(Dataset: TObject; Value1, Value2: Variant; + FieldIndex: integer): Integer; + +////////////////////////////////////////////////////////////////// +// FIELD TYPES // +////////////////////////////////////////////////////////////////// +const + FIELD_TYPE_BOOL = 16; + FIELD_TYPE_BYTEA = 17; + FIELD_TYPE_CHAR = 18; + FIELD_TYPE_NAME = 19; + FIELD_TYPE_INT8 = 20; + FIELD_TYPE_INT2 = 21; + FIELD_TYPE_INT2VECTOR = 22; + FIELD_TYPE_INT4 = 23; + FIELD_TYPE_REGPROC = 24; + FIELD_TYPE_TEXT = 25; + FIELD_TYPE_OID = 26; + FIELD_TYPE_TID = 27; + FIELD_TYPE_XID = 28; + FIELD_TYPE_CID = 29; + FIELD_TYPE_OIDVECTOR = 30; + FIELD_TYPE_SET = 32; + FIELD_TYPE_SMGR = 210; + FIELD_TYPE_POINT = 600; + FIELD_TYPE_LSEG = 601; + FIELD_TYPE_PATH = 602; + FIELD_TYPE_BOX = 603; + FIELD_TYPE_POLYGON = 604; + FIELD_TYPE_LINE = 628; + FIELD_TYPE_A_LINE = 629; + FIELD_TYPE_CIDR = 650; + FIELD_TYPE_A_CIDR = 651; + FIELD_TYPE_FLOAT4 = 700; + FIELD_TYPE_FLOAT8 = 701; + FIELD_TYPE_ABSTIME = 702; + FIELD_TYPE_RELTIME = 703; + FIELD_TYPE_TINTERVAL = 704; + FIELD_TYPE_UNKNOWN = 705; + FIELD_TYPE_CIRCLE = 718; + FIELD_TYPE_A_CIRCLE = 719; + FIELD_TYPE_MONEY = 790; + FIELD_TYPE_A_MONEY = 791; + FIELD_TYPE_MACADDR = 829; + FIELD_TYPE_MACADDR8 = 774; + FIELD_TYPE_INET = 869; + FIELD_TYPE_A_BOOL = 1000; + FIELD_TYPE_A_BYTEA = 1001; + FIELD_TYPE_A_CHAR = 1002; + FIELD_TYPE_A_NAME = 1003; + FIELD_TYPE_A_INT2 = 1005; + FIELD_TYPE_A_INT28 = 1006; + FIELD_TYPE_A_INT4 = 1007; + FIELD_TYPE_A_REGPROC = 1008; + FIELD_TYPE_A_TEXT = 1009; + FIELD_TYPE_A_TID = 1010; + FIELD_TYPE_A_XID = 1011; + FIELD_TYPE_A_CID = 1012; + FIELD_TYPE_A_OID8 = 1013; + FIELD_TYPE_A_BPCHAR = 1014; + FIELD_TYPE_A_VARCHAR = 1015; + FIELD_TYPE_A_POINT = 1017; + FIELD_TYPE_A_LSEG = 1018; + FIELD_TYPE_A_PATH = 1019; + FIELD_TYPE_A_BOX = 1020; + FIELD_TYPE_A_FLOAT4 = 1021; + FIELD_TYPE_A_FLOAT8 = 1022; + FIELD_TYPE_A_ABSTIME = 1023; + FIELD_TYPE_A_RELTIME = 1024; + FIELD_TYPE_A_TINTERVAL = 1025; + FIELD_TYPE_A_FILENAME = 1026; + FIELD_TYPE_A_POLYGON = 1027; + FIELD_TYPE_A_OID = 1028; + FIELD_TYPE_ACLITEM = 1033; + FIELD_TYPE_A_ACLITEM = 1034; + FIELD_TYPE_A_MACADDR = 1040; + FIELD_TYPE_A_MACADDR8 = 775; + FIELD_TYPE_A_INET = 1041; + FIELD_TYPE_BPCHAR = 1042; + FIELD_TYPE_VARCHAR = 1043; + FIELD_TYPE_DATE = 1082; + FIELD_TYPE_TIME = 1083; + FIELD_TYPE_A_TIMESTAMP = 1115; + FIELD_TYPE_TIMESTAMP = 1114; + FIELD_TYPE_A_DATE = 1182; + FIELD_TYPE_A_TIME = 1183; + FIELD_TYPE_A_TIMESTAMPTZ = 1185; + FIELD_TYPE_TIMESTAMPTZ = 1184; + FIELD_TYPE_A_DATETIME = 1185; + FIELD_TYPE_INTERVAL = 1186; + FIELD_TYPE_A_INTERVAL = 1187; + FIELD_TYPE_A_TIMETZ = 1270; + FIELD_TYPE_TIMETZ = 1266; + FIELD_TYPE_A_BIT = 1561; + FIELD_TYPE_BIT = 1560; + FIELD_TYPE_A_VARBIT = 1563; + FIELD_TYPE_VARBIT = 1562; + FIELD_TYPE_A_NUMERIC = 1231; + FIELD_TYPE_NUMERIC = 1700; + FIELD_TYPE_UUID = 2950; + FIELD_TYPE_JSON = 114; + FIELD_TYPE_XML = 142; + FIELD_TYPE_TSVECTOR = 3614; + FIELD_TYPE_GTSVECTOR = 3642; + FIELD_TYPE_TSQUERY = 3615; + FIELD_TYPE_REGCONFIG = 3734; + FIELD_TYPE_REGDICTIONARY = 3769; + FIELD_TYPE_JSONB = 3802; + + //range types + FIELD_TYPE_INT4RANGE = 3904; + FIELD_TYPE_NUMRANGE = 3906; + FIELD_TYPE_TSRANGE = 3908; + FIELD_TYPE_TSTZRANGE = 3910; + FIELD_TYPE_DATERANGE = 3912; + FIELD_TYPE_INT8RANGE = 3926; + FIELD_TYPE_A_INT8RANGE = 3927; + + + PSEUDO_TYPE_VOID = 2278; + PSEUDO_TYPE_TRIGGER = 2279; + PSEUDO_TYPE_LANGHANDLER = 2280; + PSEUDO_TYPE_RECORD = 2249; + PSEUDO_TYPE_CSTRING = 2275; + PSEUDO_TYPE_A_CSTRING = 1263; + PSEUDO_TYPE_INTERNAL = 2281; + PSEUDO_TYPE_ANYENUM = 3500; + PSEUDO_ANY_ARRAY = 2277; + PSEUDO_ANY_NONARRAY = 2276; + PSEUDO_ANY_ELEMENT = 2283; + PSEUDO_OPAQUE = 2282; + PSEUDO_ANY_ENUM = 3500; + + MAX_BUILTIN_TYPE_OID = FIELD_TYPE_A_INT8RANGE; //pg: 04.04.2012 need to be changed if new built-in type appears + + + MAXARRFLDTYPES = 38; + + FldArrayType: array[0..MAXARRFLDTYPES-1] of Cardinal = ( + FIELD_TYPE_A_LINE, FIELD_TYPE_A_CIDR, FIELD_TYPE_A_CIRCLE, FIELD_TYPE_A_MONEY, FIELD_TYPE_A_BOOL, FIELD_TYPE_A_BYTEA, + FIELD_TYPE_A_CHAR, FIELD_TYPE_A_NAME, FIELD_TYPE_A_INT2, FIELD_TYPE_A_INT28, FIELD_TYPE_A_INT4, FIELD_TYPE_A_REGPROC, + FIELD_TYPE_A_TEXT, FIELD_TYPE_A_TID, FIELD_TYPE_A_XID, FIELD_TYPE_A_CID, FIELD_TYPE_A_OID8, FIELD_TYPE_A_BPCHAR, + FIELD_TYPE_A_VARCHAR, FIELD_TYPE_A_POINT, FIELD_TYPE_A_LSEG, FIELD_TYPE_A_PATH, FIELD_TYPE_A_BOX, FIELD_TYPE_A_FLOAT4, + FIELD_TYPE_A_FLOAT8, FIELD_TYPE_A_ABSTIME,FIELD_TYPE_A_RELTIME,FIELD_TYPE_A_TINTERVAL,FIELD_TYPE_A_FILENAME,FIELD_TYPE_A_POLYGON, + FIELD_TYPE_A_OID, FIELD_TYPE_A_ACLITEM,FIELD_TYPE_A_MACADDR,FIELD_TYPE_A_INET, FIELD_TYPE_A_DATE, FIELD_TYPE_A_TIME, + FIELD_TYPE_A_DATETIME,FIELD_TYPE_A_INTERVAL); + +////////////////////////////////////////////////////////////////// +// Collation Constants // +////////////////////////////////////////////////////////////////// +const + DEFAULT_COLLATION_OID = 100; + C_COLLATION_OID = 950; + POSIX_COLLATION_OID = 951; + +////////////////////////////////////////////////////////////////// +// Plain API Types definition // +////////////////////////////////////////////////////////////////// +type + + //used to determine what native type used to store BLOBs + TNativeBLOBType = (nbtNotBLOB, nbtBytea, nbtOID); + + //used to determine what native presentation used for Bytea + TNativeByteaFormat = (nbfEscape, nbfHex); + + + MemPtr = ^MemArray; + MemArray = array[0..MaxInt-1] of Byte; + + Oid = Cardinal; + POid = ^Oid; + TDynOidArray = array of Oid; + + ConnStatusType = ( + CONNECTION_OK, + CONNECTION_BAD, + //Non-blocking mode only below here + CONNECTION_STARTED, // Waiting for connection to be made + CONNECTION_MADE, // Connection OK; waiting to send + CONNECTION_AWAITING_RESPONSE, // Waiting for a response from the postmaster + CONNECTION_AUTH_OK, // Received authentication; waiting for backend startup + CONNECTION_SETENV, // Negotiating environment + CONNECTION_SSL_STARTUP, // Negotiating SSL + CONNECTION_NEEDED // Internal state: connect() needed + ); + + PollingStatusType = ( + PGRES_POLLING_FAILED, + PGRES_POLLING_READING, // These two indicate that one may + PGRES_POLLING_WRITING, // use select before polling again + PGRES_POLLING_OK, + PGRES_POLLING_ACTIVE // unused; keep for awhile for backwards compatibility + ); + + ExecStatusType = ( + PGRES_EMPTY_QUERY, + PGRES_COMMAND_OK, // a query command that doesn't return anything was executed properly by the backend + PGRES_TUPLES_OK, // a query command that returns tuples was executed properly by the backend, PGresult contains the result tuples + PGRES_COPY_OUT, // Copy Out data transfer in progress + PGRES_COPY_IN, // Copy In data transfer in progress + PGRES_BAD_RESPONSE, // an unexpected response was recv'd from the backend + PGRES_NONFATAL_ERROR, // notice or warning message + PGRES_FATAL_ERROR, //query failed + PGRES_COPY_BOTH, // Copy In/Out data transfer in progress + PGRES_SINGLE_TUPLE // single tuple from larger resultset + ); + +// String descriptions of the ExecStatusTypes + pgresStatus = array[$00..$ff] of PAnsiDACChar; + + TErrorVerbosity = (evTERSE, evDEFAULT, evVERBOSE); + + TTransactionStatusType = ( + trstIDLE, // connection idle + trstACTIVE, // command in progress + trstINTRANS, // idle, within transaction block + trstINERROR, // idle, within failed transaction + trstUNKNOWN); // cannot determine status + + TPingStatus = ( + pstOK, //The server is running and appears to be accepting connections + pstReject, //The server is running but is in a state that disallows connections (startup, shutdown, or crash recovery) + pstNoResponse, //The server could not be contacted + pstNoAttempt); //No attempt was made to contact the server due to incorrect parameters + +//////////////////////////////////////////////////////////////////// +// PGconn encapsulates a connection to the backend. // +// The contents of this struct are not supposed to be known to // +// applications. // +//////////////////////////////////////////////////////////////////// + PGconn = Pointer; + PPGconn = Pointer; + +// PGresult encapsulates the result of a query (or more precisely, of a single +// SQL command --- a query string given to PQsendQuery can contain multiple +// commands and thus return multiple PGresult objects). +// The contents of this struct are not supposed to be known to applications. + PGresult = Pointer; + PPGresult = ^PGresult; + +// PGnotify represents the occurrence of a NOTIFY message. +// Ideally this would be an opaque typedef, but it's so simple that it's +// unlikely to change. +// NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's, +// whereas in earlier versions it was always your own backend's PID. + PPGnotify = ^PGnotify; + PGnotify = record + relname: PAnsiDACChar; // name of relation containing data + be_pid: Integer; // process id of backend + extra: PAnsiDACChar; // extra notification + next: PPGnotify; // application should never use this + end; + + +// PQnoticeProcessor is the function type for the notice-message callback. + PQnoticeProcessor = procedure(arg: Pointer; message: PAnsiDACChar);cdecl; + +// Print options for PQprint() +// We can't use the conventional "bool", because we are designed to be +// included in a user's program, and user may already have that type +// defined. Pqbool, on the other hand, is unlikely to be used. + + PPAnsiDACChar = ^PAnsiDACChar; + + PQprintOpt = packed record + header: Byte; { print output field headings and row count } + align: Byte; { fill align the fields } + standard: Byte; { old brain dead format } + html3: Byte; { output html tables } + expanded: Byte; { expand tables } + pager: Byte; { use pager for output if needed } + fieldSep: PAnsiDACChar; { field separator } + tableOpt: PAnsiDACChar; { insert to HTML } + caption: PAnsiDACChar; { HTML
} + fieldName: PPAnsiDACChar; { null terminated array of repalcement field names } + end; + + PPQprintOpt = ^PQprintOpt; + +////////////////////////////////////////////////////////////////////////////////// +// Structure for the conninfo parameter definitions returned by PQconndefaults // +////////////////////////////////////////////////////////////////////////////////// + PQconninfoOption = packed record + keyword: PAnsiDACChar; { The keyword of the option } + envvar: PAnsiDACChar; { Fallback environment variable name } + compiled: PAnsiDACChar; { Fallback compiled in default value } + val: PAnsiDACChar; { Options value } + lab: PAnsiDACChar; { Label for field in connect dialog } + dispchar: PAnsiDACChar; { Character to display for this field + in a connect dialog. Values are: + "" Display entered value as is + "*" Password field - hide value + "D" Debug options - don't + create a field by default } + dispsize: Integer; { Field size in characters for dialog } + end; + + PPQConninfoOption = ^PQconninfoOption; + +////////////////////////////////////////////////////////////////// +// Plain API Function types definition // +////////////////////////////////////////////////////////////////// + TPQlibVersion = function(): Integer; cdecl; + TPQisthreadsafe = function(): Integer; cdecl; + TPQconnectdb = function(ConnInfo: PAnsiDACChar): PPGconn; cdecl; //blocking manner + TPQconnectStart = function(ConnInfo: PAnsiDACChar): PPGconn; cdecl; //non-blocking manner + TPQconnectdbParams = function(Keywords: PPAnsiDACChar; Values: PPAnsiDACChar; ExpandDBName: integer): PPGconn; cdecl; //blocking manner + TPQping = function(ConnInfo: PAnsiDACChar): TPingStatus; cdecl; + TPQpingParams = function(Keywords: PPAnsiDACChar; Values: PPAnsiDACChar; ExpandDBName: integer): TPingStatus; + TPQconnectPoll = function (Handle : PPGconn): PollingStatusType; cdecl; + TPQsetdbLogin = function(Host, Port, Options, Tty, Db, User, Passwd: PAnsiDACChar): PPGconn; cdecl; + TPQconndefaults = function: PPQconninfoOption; cdecl; + TPQfinish = procedure(Handle: PPGconn); cdecl; + TPQreset = procedure(Handle: PPGconn); cdecl; + TPQrequestCancel = function(Handle: PPGconn): Integer; cdecl; + TPQdb = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQuser = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQpass = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQhost = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQport = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQtty = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQoptions = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQstatus = function(Handle: PPGconn): ConnStatusType; cdecl; + TPQerrorMessage = function(Handle: PPGconn): PAnsiDACChar; cdecl; + TPQsocket = function(Handle: PPGconn): Integer; cdecl; + TPQbackendPID = function(Handle: PPGconn): Integer; cdecl; + TPQparameterStatus = function(Handle: PPGconn; paramName: PAnsiDACChar): PAnsiDACChar; cdecl; + TPQserverVersion = function(Handle: PPGconn): Integer; cdecl; + TPQtransactionStatus = function(Handle: PPGconn): TTransactionStatusType; cdecl; + TPQgetssl = function(Handle: PPGconn): pointer; cdecl; //point to SSL structure, see OpenSSL manual for details + TPQtrace = procedure(Handle: PPGconn; DebugPort: Pointer); cdecl; + TPQuntrace = procedure(Handle: PPGconn); cdecl; + TPQsetNoticeProcessor = function(Handle: PPGconn; Proc: PQnoticeProcessor; Arg: Pointer): Pointer; cdecl; + TPQprepare = function(Handle: PPGconn; + StmtName: PAnsiDACChar; + Query: PAnsiDACChar; + nParams: integer; + paramTypes: POid): PPGresult; cdecl; + TPQexecPrepared = function(Handle: PPGconn; + StmtName: PAnsiDACChar; + nParams: integer; + paramValues: PPAnsiDACChar; + paramLengths: PInteger; + paramFormats: PInteger; + resultFormat: integer): PPGresult; cdecl; + + TPQexec = function(Handle: PPGconn; Query: PAnsiDACChar): PPGresult; cdecl; + + TPQexecParams = function(Handle: PPGconn; + Query: PAnsiDACChar; + nParams: integer; + paramTypes: POid; + paramValues: PPAnsiDACChar; + paramLengths: PInteger; + paramFormats: PInteger; + resultFormat: integer): PPGresult; cdecl; + TPQresultErrorField = function(Result: PPGresult; fieldcode: integer): PAnsiDACChar; cdecl; + TPQnotifies = function(Handle: PPGconn): PPGnotify; cdecl; + TPQsetSingleRowMode = function(Handle: PPGconn): integer; cdecl; + TPQsendQuery = function(Handle: PPGconn; Query: PAnsiDACChar): Integer; cdecl; + TPQgetResult = function(Handle: PPGconn): PPGresult; cdecl; + TPQisBusy = function(Handle: PPGconn): Integer; cdecl; + TPQconsumeInput = function(Handle: PPGconn): Integer; cdecl; + TPQgetline = function(Handle: PPGconn; + Str: PAnsiDACChar; + length: Integer): Integer; cdecl; + TPQputline = function(Handle: PPGconn; + Str: PAnsiDACChar): Integer; cdecl; + TPQgetlineAsync = function(Handle: PPGconn; + Buffer: PAnsiDACChar; + BufSize: Integer): Integer; cdecl; + TPQputnbytes = function(Handle: PPGconn; + Buffer: PAnsiDACChar; + NBytes: Integer): Integer; cdecl; + TPQendcopy = function(Handle: PPGconn): Integer; cdecl; + TPQgetCopyData = function(Handle: PPGConn; + Buffer: PPAnsiDACChar; + Async: integer = 0): Integer; cdecl; + TPQputCopyData = function(Handle: PPGConn; + Buffer: PAnsiDACChar; + Len: integer): Integer; cdecl; + TPQputCopyEnd = function(Handle: PPGConn; + Buffer: PAnsiDACChar = nil): Integer; cdecl; + TPQresultStatus = function(Result: PPGresult): ExecStatusType; cdecl; + TPQresultErrorMessage = function(Result: PPGresult): PAnsiDACChar; cdecl; + TPQntuples = function(Result: PPGresult): Integer; cdecl; + TPQnfields = function(Result: PPGresult): Integer; cdecl; + TPQbinaryTuples = function(Result: PPGresult): Integer; cdecl; + TPQfname = function(Result: PPGresult; field_num: Integer): PAnsiDACChar; cdecl; + TPQfnumber = function(Result: PPGresult; field_name: PAnsiDACChar): Integer; cdecl; + TPQftype = function(Result: PPGresult; field_num: Integer): Oid; cdecl; + TPQfformat = function(Result: PPGresult; field_num: Integer): Oid; cdecl; + TPQftable = function(Result: PPGresult; field_num: Integer): Oid; cdecl; + TPQftablecol = function(Result: PPGresult; field_num: Integer): Integer; cdecl; + TPQfsize = function(Result: PPGresult; field_num: Integer): Integer; cdecl; + TPQfmod = function(Result: PPGresult; field_num: Integer): Integer; cdecl; + TPQcmdStatus = function(Result: PPGresult): PAnsiDACChar; cdecl; + TPQoidValue = function(Result: PPGresult): Oid; cdecl; + TPQoidStatus = function(Result: PPGresult): PAnsiDACChar; cdecl; + TPQcmdTuples = function(Result: PPGresult): PAnsiDACChar; cdecl; + TPQgetvalue = function(Result: PPGresult; + tup_num: Integer; + field_num: Integer): PAnsiDACChar; cdecl; + TPQsetvalue = function(Result: PPGresult; + tup_num: Integer; + field_num: Integer; + value: PAnsiDACChar; + len: integer): integer; cdecl; + TPQcopyResult = function(Result: PPGresult; flags: integer): PPGresult; cdecl; + TPQgetlength = function(Result: PPGresult; + tup_num: Integer; + field_num: Integer): Integer; cdecl; + TPQgetisnull = function(Result: PPGresult; + tup_num: Integer; + field_num: Integer): Integer; cdecl; + TPQclear = procedure(Result: PPGresult); cdecl; + TPQmakeEmptyPGresult = function(Handle: PPGconn; + status: ExecStatusType): PPGresult; cdecl; + TPQEscapeByteaConn = function(Handle: PPGconn; + from: PAnsiDACChar; + from_length: integer; + var to_length: integer): PAnsiDACChar; cdecl; + TPQUnEscapeBytea = function(from: PAnsiDACChar; var to_length: integer): PAnsiDACChar; cdecl; + TPQEscapeStringConn = function(Handle: PPGconn; + to_str: PAnsiDACChar; + const from_str: PAnsiDACChar; + from_size: cardinal; + var Error: integer): cardinal; cdecl; + TPQFreeMem = procedure(Ptr: Pointer); cdecl; + TPQsetClientEncoding = function(Handle: PPGconn; encoding: PAnsiDACChar): integer; cdecl; + TPQsetErrorVerbosity = function(Handle: PPGconn; verbosity: TErrorVerbosity): TErrorVerbosity; cdecl; + TPQclientEncoding = function(Handle: PPGconn): integer; cdecl; + Tpg_encoding_to_char = function(encoding_id: integer): PAnsiDACChar; cdecl; + Tlo_open = function(Handle: PPGconn; + lobjId: Oid; + mode: Integer): Integer; cdecl; + Tlo_close = function(Handle: PPGconn; fd: Integer): Integer; cdecl; + Tlo_read = function(Handle: PPGconn; + fd: Integer; + buf: PAnsiDACChar; + len: Integer): Integer; cdecl; + Tlo_write = function(Handle: PPGconn; + fd: Integer; + buf: PAnsiDACChar; + len: Integer): Integer; cdecl; + Tlo_lseek = function(Handle: PPGconn; + fd: Integer; + offset: Integer; + whence: Integer): Integer; cdecl; + Tlo_creat = function(Handle: PPGconn; mode: Integer): Oid; cdecl; + Tlo_tell = function(Handle: PPGconn; fd: Integer): Integer; cdecl; + Tlo_unlink = function(Handle: PPGconn; lobjId: Oid): Integer; cdecl; + Tlo_import = function(Handle: PPGconn; filename: PAnsiDACChar): Oid; cdecl; + Tlo_export = function(Handle: PPGconn; lobjId: Oid; filename: PAnsiDACChar): Integer; cdecl; + + +////////////////////////////////////////////////////////////////// +// Plain API function variables definition // +////////////////////////////////////////////////////////////////// + +var + PQlibVersion: TPQlibVersion; + PQisthreadsafe: TPQisthreadsafe; + PQconnectdb: TPQconnectdb; + PQconnectdbParams: TPQconnectdbParams; + PQconnectStart: TPQconnectStart; + PQping: TPQping; + PQpingParams: TPQpingParams; + PQconnectPoll: TPQconnectPoll; + PQsetdbLogin: TPQsetdbLogin; + PQconndefaults: TPQconndefaults; + PQfinish: TPQfinish; + PQreset: TPQreset; + PQrequestCancel: TPQrequestCancel; + PQdb: TPQdb; + PQuser: TPQuser; + PQpass: TPQpass; + PQhost: TPQhost; + PQport: TPQport; + PQtty: TPQtty; + PQoptions: TPQoptions; + PQstatus: TPQstatus; + PQerrorMessage: TPQerrorMessage; + PQsocket: TPQsocket; + PQparameterStatus: TPQparameterStatus; + PQserverVersion: TPQserverVersion; + PQbackendPID: TPQbackendPID; + PQtransactionStatus: TPQtransactionStatus; + PQgetssl: TPQgetssl; + PQtrace: TPQtrace; + PQuntrace: TPQuntrace; + PQsetNoticeProcessor: TPQsetNoticeProcessor; + PQprepare: TPQprepare; + PQexecPrepared: TPQexecPrepared; + PQexec: TPQexec; + PQsendQuery: TPQsendQuery; + PQexecParams: TPQexecParams; + PQresultErrorField:TPQresultErrorField; + PQnotifies: TPQnotifies; + PQsetSingleRowMode: TPQsetSingleRowMode; + PQgetResult: TPQgetResult; + PQisBusy: TPQisBusy; + PQconsumeInput: TPQconsumeInput; + PQgetline: TPQgetline; + PQputline: TPQputline; + PQgetlineAsync: TPQgetlineAsync; + PQputnbytes: TPQputnbytes; + PQendcopy: TPQendcopy; + PQgetCopyData: TPQgetCopyData; + PQputCopyData: TPQputCopyData; + PQputCopyEnd: TPQputCopyEnd; + PQresultStatus: TPQresultStatus; + PQresultErrorMessage: TPQresultErrorMessage; + PQntuples: TPQntuples; + PQnfields: TPQnfields; + PQbinaryTuples: TPQbinaryTuples; + PQfname: TPQfname; + PQfnumber: TPQfnumber; + PQftype: TPQftype; + PQfformat: TPQfformat; + PQftable: TPQftable; + PQftablecol: TPQftablecol; + PQfsize: TPQfsize; + PQfmod: TPQfmod; + PQcmdStatus: TPQcmdStatus; + PQoidValue: TPQoidValue; + PQoidStatus: TPQoidStatus; + PQcmdTuples: TPQcmdTuples; + PQgetvalue: TPQgetvalue; + PQsetvalue: TPQsetvalue; + PQcopyResult: TPQcopyResult; + PQgetlength: TPQgetlength; + PQgetisnull: TPQgetisnull; + PQclear: TPQclear; + PQmakeEmptyPGresult: TPQmakeEmptyPGresult; + PQEscapeByteaConn: TPQEscapeByteaConn; + PQUnEscapeBytea: TPQUnEscapeBytea; + PQEscapeStringConn: TPQEscapeStringConn; + PQFreeMem: TPQFreeMem; + PQsetClientEncoding: TPQsetClientEncoding; + PQsetErrorVerbosity: TPQsetErrorVerbosity; + PQclientEncoding: TPQclientEncoding; + pg_encoding_to_char: Tpg_encoding_to_char; + lo_open: Tlo_open; + lo_close: Tlo_close; + lo_read: Tlo_read; + lo_write: Tlo_write; + lo_lseek: Tlo_lseek; + lo_creat: Tlo_creat; + lo_tell: Tlo_tell; + lo_unlink: Tlo_unlink; + lo_import: Tlo_import; + lo_export: Tlo_export; + +///////////////////////////////////////////////////////////////////////////////// +// BDE TYPE // +///////////////////////////////////////////////////////////////////////////////// + +resourcestring + SAutoSessionExclusive = 'Cannot enable AutoSessionName property with more than one session on a form or data-module'; + SAutoSessionExists = 'Cannot add a session to the form or data-module while session ''%s'' has AutoSessionName enabled'; + SAutoSessionActive = 'Cannot modify SessionName while AutoSessionName is enabled'; + SDuplicateDatabaseName = 'Duplicate database name ''%s'''; + SDuplicateSessionName = 'Duplicate session name ''%s'''; + SInvalidSessionName = 'Invalid session name %s'; + SDatabaseNameMissing = 'Database name missing'; + SSessionNameMissing = 'Session name missing'; + SDatabaseOpen = 'Cannot perform this operation on an open database'; + SDatabaseClosed = 'Cannot perform this operation on a closed database'; + SDatabaseHandleSet = 'Database handle owned by a different session'; + SSessionActive = 'Cannot perform this operation on an active session'; + SHandleError = 'Error creating cursor handle'; + SInvalidFloatField = 'Cannot convert field ''%s'' to a floating point value'; + SInvalidIntegerField = 'Cannot convert field ''%s'' to an integer value'; + SInvalidRangeType = 'Cannot convert value. Unsupported range type passed'; + STableMismatch = 'Source and destination tables are incompatible'; + SFieldAssignError = 'Fields ''%s'' and ''%s'' are not assignment compatible'; + SFieldNotRangeType = 'Field ''%s'' is not range type'; + SNoReferenceTableName = 'ReferenceTableName not specified for field ''%s'''; + SCompositeIndexError = 'Cannot use array of Field values with Expression Indices'; + SInvalidBatchMove = 'Invalid batch move parameters'; + SEmptySQLStatement = 'No SQL statement available'; + SNoParameterValue = 'No value for parameter ''%s'''; + SNoParameterType = 'No parameter type for parameter ''%s'''; + SLoginError = 'Cannot connect to database ''%s'''; + SLoginPrompt = 'Cannot call login prompt to database ''%s'''; + SInitError = 'An error occurred while attempting to initialize the Borland Database Engine (error $%.4x)'; + SDatabaseEditor = 'Da&tabase Editor...'; + SExplore = 'E&xplore'; + SLinkDetail = '''%s'' cannot be opened'; + SLinkMasterSource = 'The MasterSource property of ''%s'' must be linked to a DataSource'; + SLinkMaster = 'Unable to open the MasterSource Table'; + SGQEVerb = 'S&QL Builder...'; + SBindVerb = 'Define &Parameters...'; + SIDAPILangID = '0009'; + SDisconnectDatabase = 'Database is currently connected. Disconnect and continue?'; + SBDEError = 'BDE error $%.4x'; + SLookupSourceError = 'Unable to use duplicate DataSource and LookupSource'; + SLookupTableError = 'LookupSource must be connected to TTable component'; + SLookupIndexError = '%s must be the lookup table''s active index'; + SParameterTypes = ';Input;Output;Input/Output;Result'; + SInvalidParamFieldType = 'Must have a valid field type selected'; + STruncationError = 'Parameter ''%s'' truncated on output'; + SDataTypes = ';String;SmallInt;Integer;Word;Boolean;Float;Currency;BCD;Date;Time;DateTime;;;;Blob;Memo;Graphic;;;;;Cursor;'; + SResultName = 'Result'; + SDBCaption = '%s%s%s Database'; + SParamEditor = '%s%s%s Parameters'; + SIndexFilesEditor = '%s%s%s Index Files'; + SNoIndexFiles = '(None)'; + SIndexDoesNotExist = 'Index does not exist. Index: %s'; + SNoTableName = 'Missing TableName property'; + SNoDataSetField = 'Missing DataSetField property'; + SBatchExecute = 'E&xecute'; + SNoCachedUpdates = 'Not in cached update mode'; + SInvalidAliasName = 'Invalid alias name %s'; + SNoFieldAccess = 'Cannot access field ''%s'' in a filter'; + SUpdateSQLEditor = '&UpdateSQL Editor...'; + SNoDataSet = 'No dataset association'; + SUntitled = 'Untitled Application'; + SUpdateWrongDB = 'Cannot update, %s is not owned by %s'; + SUpdateFailed = 'Update failed'; + SSQLGenSelect = 'Must select at least one key field and one update field'; + SSQLNotGenerated = 'Update SQL statements not generated, exit anyway?'; + SSQLDataSetOpen = 'Unable to determine field names for %s'; + SLocalTransDirty = 'The transaction isolation level must be dirty read for local databases'; + SMissingDataSet = 'Missing DataSet property'; + SNoProvider = 'No provider available'; + SNotAQuery = 'Dataset is not a query'; + + SInvalidFieldSize = 'Invalid field size'; + SInvalidFieldKind = 'Invalid FieldKind'; + SInvalidFieldRegistration = 'Invalid field registration'; + SUnknownFieldType = 'Field ''%s'' is of an unknown type'; + SFieldNameMissing = 'Field name missing'; + SDuplicateFieldName = 'Duplicate field name ''%s'''; + SFieldNotFound = 'Field ''%s'' not found'; + SFieldAccessError = 'Cannot access field ''%s'' as type %s'; + SFieldValueError = 'Invalid value for field ''%s'''; + SFieldRangeError = '%g is not a valid value for field ''%s''. The allowed range is %g to %g'; + SBcdFieldRangeError = '%s is not a valid value for field ''%s''. The allowed range is %s to %s'; + SInvalidIntegerValue = '''%s'' is not a valid integer value for field ''%s'''; + SInvalidBoolValue = '''%s'' is not a valid boolean value for field ''%s'''; + SInvalidFloatValue = '''%s'' is not a valid floating point value for field ''%s'''; + SFieldTypeMismatch = 'Type mismatch for field ''%s'', expecting: %s actual: %s'; + SFieldSizeMismatch = 'Size mismatch for field ''%s'', expecting: %d actual: %d'; + SInvalidVarByteArray = 'Invalid variant type or size for field ''%s'''; + SFieldOutOfRange = 'Value of field ''%s'' is out of range'; +// SBCDOverflow = '(Overflow)'; + SCantAdjustPrecision = 'Error adjusting BCD precision'; + SFieldRequired = 'Field ''%s'' must have a value'; + SDataSetMissing = 'Field ''%s'' has no dataset'; + SInvalidCalcType = 'Field ''%s'' cannot be a calculated or lookup field'; + SFieldReadOnly = 'Field ''%s'' cannot be modified'; + SFieldIndexError = 'Field index out of range'; + SNoFieldIndexes = 'No index currently active'; + SNotIndexField = 'Field ''%s'' is not indexed and cannot be modified'; + SIndexFieldMissing = 'Cannot access index field ''%s'''; + SDuplicateIndexName = 'Duplicate index name ''%s'''; + SNoIndexForFields = 'No index for fields ''%s'''; + SIndexNotFound = 'Index ''%s'' not found'; + SDBDuplicateName = 'Duplicate name ''%s'' in %s'; + SCircularDataLink = 'Circular datalinks are not allowed'; + SLookupInfoError = 'Lookup information for field ''%s'' is incomplete'; + SNewLookupFieldCaption = 'New Lookup Field'; + SDataSourceChange = 'DataSource cannot be changed'; + SNoNestedMasterSource = 'Nested datasets cannot have a MasterSource'; + SDataSetOpen = 'Cannot perform this operation on an open dataset'; + SNotEditing = 'Dataset not in edit or insert mode'; + SDataSetClosed = 'Cannot perform this operation on a closed dataset'; + SDataSetEmpty = 'Cannot perform this operation on an empty dataset'; + SDataSetReadOnly = 'Cannot modify a read-only dataset'; + SNestedDataSetClass = 'Nested dataset must inherit from %s'; + SExprTermination = 'Filter expression incorrectly terminated'; + SExprNameError = 'Unterminated field name'; + SExprStringError = 'Unterminated string constant'; + SExprInvalidChar = 'Invalid filter expression character: ''%s'''; + SExprNoLParen = '''('' expected but %s found'; + SExprNoRParen = ''')'' expected but %s found'; + SExprNoRParenOrComma = ''')'' or '','' expected but %s found'; + SExprExpected = 'Expression expected but %s found'; + SExprBadField = 'Field ''%s'' cannot be used in a filter expression'; + SExprBadNullTest = 'NULL only allowed with ''='' and ''<>'''; + SExprRangeError = 'Constant out of range'; + SExprNotBoolean = 'Field ''%s'' is not of type Boolean'; + SExprIncorrect = 'Incorrectly formed filter expression'; + SExprNothing = 'nothing'; + SExprTypeMis = 'Type mismatch in expression'; + SExprBadScope = 'Operation cannot mix aggregate value with record-varying value'; + SExprNoArith = 'Arithmetic in filter expressions not supported'; + SExprNotAgg = 'Expression is not an aggregate expression'; + SExprBadConst = 'Constant is not correct type %s'; + SExprNoAggFilter = 'Aggregate expressions not allowed in filters'; + SExprEmptyInList = 'IN predicate list may not be empty'; + SInvalidKeywordUse = 'Invalid use of keyword'; + STextFalse = 'False'; + STextTrue = 'True'; + SParameterNotFound = 'Parameter ''%s'' not found'; + SInvalidVersion = 'Unable to load bind parameters'; + SParamTooBig = 'Parameter ''%s'', cannot save data larger than %d bytes'; + SBadFieldType = 'Field ''%s'' is of an unsupported type'; + SAggActive = 'Property may not be modified while aggregate is active'; + SProviderSQLNotSupported = 'SQL not supported'; + SProviderExecuteNotSupported = 'Execute not supported'; + SExprNoAggOnCalcs = 'Field ''%s'' is not the correct type of calculated field to be used in an aggregate, use an internalcalc'; + SRecordChanged = 'Record not found or changed by another user'; + SDataSetUnidirectional = 'Operation not allowed on a unidirectional dataset'; + SUnassignedVar = 'Unassigned variant value'; + SRecordNotFound = 'Record not found'; + SFileNameBlank = 'FileName property cannot be blank'; + SFieldNameTooLarge = 'Fieldname %s exceeds %d chars'; + +{ For FMTBcd } + + SBcdOverflow = 'BCD overflow'; + SInvalidBcdValue = '%s is not a valid BCD value'; + SInvalidFormatType = 'Invalid format type for BCD'; + +{ For SqlTimSt } + + SCouldNotParseTimeStamp = 'Could not parse SQL TimeStamp string'; + SInvalidSqlTimeStamp = 'Invalid SQL date/time values'; + SCalendarTimeCannotBeRepresented = 'Calendar time cannot be represented'; + + SDeleteRecordQuestion = 'Delete record?'; + SDeleteMultipleRecordsQuestion = 'Delete all selected records?'; + STooManyColumns = 'Grid requested to display more than 256 columns'; + + { For reconcile error } + SSkip = 'Skip'; + SAbort = 'Abort'; + SMerge = 'Merge'; + SCorrect = 'Correct'; + SCancel = 'Cancel'; + SRefresh = 'Refresh'; + SModified = 'Modified'; + SInserted = 'Inserted'; + SDeleted = 'Deleted'; + SCaption = 'Update Error - %s'; + SUnchanged = ''; + SBinary = '(Binary)'; + SAdt = '(ADT)'; + SArray = '(Array)'; + SFieldName = 'Field Name'; + SOriginal = 'Original Value'; + SConflict = 'Conflicting Value'; + SValue = ' Value'; + SNoData = ''; + SNew = 'New'; + +//-----------------------------------------------------------------------// +// DBI types // +//-----------------------------------------------------------------------// + +const + DBIMAXNAMELEN = 63;{31;} { Name limit (table, field etc) } + DBIMAXSPNAMELEN = 64; { Max stored procedure name length } + DBIMAXFLDSINKEY = 16; { Max fields in a key } + DBIMAXKEYEXPLEN = 220; { Max Key expression length } + DBIMAXEXTLEN = 3; { Max file extension len, not incl. dot (excluding zero termination) } + DBIMAXTBLNAMELEN = 260; { Max table name length } + DBIMAXPATHLEN = 260; { Max path+file name len (excluding zero termination) } + DBIMAXMSGLEN = 127; { Max message len } + DBIMAXVCHKLEN = 255; { Max val check len } + DBIMAXPICTLEN = 175; { Max picture len } + DBIMAXFLDSINSEC = 256; { Max fields in security spec } + +Type +//============================================================================// +// G e n e r a l // +//============================================================================// + DBIDATE = Longint; + TIME = Longint; + DBIResult = Word; { function result } + TypedEnum = Integer; + + _hDBIObj = record end; { Dummy structure to create "typed" handles } + hDBIObj = ^_hDBIObj; { Generic object handle } + hDBIDb = ^_hDBIObj; { Database handle } + hDBIStmt = ^_hDBIObj; { Statement handle ("new query") } + hDBICur = ^_hDBIObj; { Cursor handle } + hDBIXact = ^_hDBIObj; { Transaction handle } + hDBIFilter = ^_hDBIObj; { Filter handle } + + +{ Handle Pointers } + phDBIObj = ^hDBIObj; { Pointer to Generic object handle } + phDBIDb = ^hDBIDb; { Pointer to Database handle } + phDBICur = ^hDBICur; { Pointer to Cursor handle } + + +{ typedefs for buffers of various common sizes: } + DBIPATH = packed array [0..DBIMAXPATHLEN] of AnsiDACChar; { holds a DOS path } + DBINAME = packed array [0..DBIMAXNAMELEN] of Char; { holds a name } + DBIEXT = packed array [0..DBIMAXEXTLEN] of PAnsiDACChar; { holds an extension EXT } + DBITBLNAME = packed array [0..DBIMAXTBLNAMELEN] of AnsiDACChar; { holds a table name } + DBISPNAME = packed array [0..DBIMAXSPNAMELEN] of AnsiDACChar; { holds a stored procedure name } + DBIKEY = packed array [0..DBIMAXFLDSINKEY-1] of Word; { holds list of fields in a key } + DBIKEYEXP = packed array [0..DBIMAXKEYEXPLEN] of AnsiDACChar; { holds a key expression } + DBIVCHK = packed array [0..DBIMAXVCHKLEN] of Byte; { holds a validity check } + DBIPICT = packed array [0..DBIMAXPICTLEN] of AnsiDACChar; { holds a picture (Pdox) } + DBIMSG = packed array [0..DBIMAXMSGLEN] of AnsiDACChar; { holds an error message } + +{============================================================================} +{ Statement parameter information } +{============================================================================} + +type + STMTParamType = ( + paramUNKNOWN, { UNKNOWN (Error) } + paramIN, { Input parameter } + paramOUT, { Output parameter } + paramINOUT, { Input/Output parameter } + paramRET { procedure (or function) return } + ); + +//============================================================================// +// General properties DbiGetProp/DbiSetProp // +//============================================================================// +{ Cursor properties } +{ General } + +const + curMAXPROPS = $00050000; { ro UINT16 , Number of defined properties } + curTABLELEVEL = $00050003; { ro UINT16 , Table level 1..n } + curXLTMODE = $00050005; { rw XLTMode , Translate mode } + curMAXFIELDID = $0005000F; { ro UINT16, Max # of field desc } + curFIELDFULLNAME = $00050010; { ro pObjAttrDesc, Object attribute name } + curFIELDTYPENAME = $00050011; { ro pObjTypeDesc, Object Type name } + curMAKECRACK = $00050014; { Create a crack at the current cursor position } + curFIELDISAUTOINCR = $00050015; { wo BOOL, Auto increment field } + curFIELDISDEFAULT = $00050016; { wo BOOL, Default field } + curAUTOREFETCH = $00050017; { rw BOOL, Refetch inserted record } + + maxcurPROPS = 23; { keep in sync when adding cursor properties } + +{ SQL Driver specific } + curUPDLOCKMODE = $04050000; { rw UPDLockMode, Update lock mode } + curGETHIDDENCOLUMNS= $04050004; { rw BOOL , Get all selected columns from server. } +{ Delayed Updates Specific. } + curDELAYUPDDISPLAYOPT = $05050003; { rw UINT16, view records } + curDELAYUPDGETOLDRECORD = $05050004; { rw BOOL, get un-modified } + curDELAYUPDNUMUPDATES = $05050005; { ro INT32, num of updates } +{ Database properties } +{ General } + dbDATABASETYPE = $00040002; { ro pDBINAME , Database type } + dbPARAMFMTQMARK = $00040004; { rw BOOL , Stmt param marker fmt = ? } + dbUSESCHEMAFILE = $00040005; { rw BOOL , for text driver only. } + +{ SQL Driver specific } + dbCOMPRESSARRAYFLDDESC = $04040011; { rw BOOL, VARRAY in compressed format, ORACLE 8 specific. } + +{ Statement properties } +{ General } + stmtUNIDIRECTIONAL = $00060010; { rw BOOL Cursor Unidirectional } + stmtROWCOUNT = $00060014; { ro UINT32 Rows effected by a stmt } + +{ specific to QBE or local SQL } + stmtLIVENESS = $00060021; { rw LIVENESS Preference for canned/live answers } + stmtAUXTBLS = $00060026; { rw BOOL True if QBE to create CHANGED, etc. } + stmtCANNEDREADONLY = $00060042; { rw BOOL canned answers are readonly } + +//============================================================================// +// Transactions // +//============================================================================// +type + eXILType = ( { Transaction isolation levels } + xilDIRTYREAD, { Uncommitted changes read } + xilREADCOMMITTED, { Committed changes, no phantoms } + xilREPEATABLEREAD, { Full read repeatability } + xilSERIALIZABLE { SERIALIZABLE } + ); + + eXEnd = ( { Transaction end control } + xendCOMMIT, { Commit transaction } + xendCOMMITKEEP, { Commit transaction, keep cursors } + xendABORT { Rollback transaction } + ); + + eXState = ( { Transaction end control } + xsINACTIVE, { Transaction inactive } + xsACTIVE { Transaction active } + ); + + pXInfo = ^XInfo; + XInfo = packed record + exState : eXState; { xsActive, xsInactive } + eXIL : eXILType; { Xact isolation level } + uNests : Word; { Xact children } + end; + +//============================================================================// +// Object types // +//============================================================================// + +type + pObjTypeDesc = ^ObjTypeDesc; + ObjTypeDesc = packed record + iFldNum : Word; { Field id } + szTypeName : DBINAME; { Object type name } + end; + + + +//============================================================================// +// Cursor properties // +//============================================================================// + +type + DBIShareMode = ( { Database/Table Share type } + dbiOPENSHARED, { Open shared (Default) } + dbiOPENEXCL { Open exclusive } + ); + + DBIOpenMode = ( { Database/Table Access type } + dbiREADWRITE, { Read + Write (Default) } + dbiREADONLY { Read only } + ); + + DBILockType = ( { Lock types (Table level) } + dbiNOLOCK, { No lock (Default) } + dbiWRITELOCK, { Write lock } + dbiREADLOCK { Read lock } + ); + + XLTMode = ( { Field translate mode } + xltNONE, { No translation (Physical Types) } + xltRECORD, { Record level translation (not supported) } + xltFIELD { Field level translation (Logical types) } + ); + + pServerColDesc = ^ServerColDesc; + ServerColDesc = packed record { Auto increment and Defaults property } + iFldNum : Word; { Field id } + bServerCol : WordBool; { Auto Increment and Default } + end; + + +type + pCURProps = ^CURProps; + CURProps = packed record { Virtual Table properties } + szName : DBITBLNAME; { table name (no extension, if it can be derived) } + iFNameSize : integer; { Full file name size } + szTableType : DBINAME; { Driver type } + iFields : integer; { No of fields in Table } + iRecSize : integer; { Record size (logical record) } + iRecBufSize : integer; { Record size (physical record) } + iKeySize : integer; { Key size } + iIndexes : integer; { Number of indexes } + iValChecks : integer; { Number of val checks } + iRefIntChecks : integer; { Number of Ref Integrity constraints } + iBookMarkSize : integer; { Bookmark size } + bBookMarkStable : WordBool; { Stable book marks } + eOpenMode : DBIOpenMode; { ReadOnly / RW } + eShareMode : DBIShareMode; { Excl / Share } + bIndexed : WordBool; { Index is in use } + iSeqNums : SmallInt; { 1: Has Seqnums; 0: Has Record# } + bSoftDeletes : WordBool; { Supports soft deletes } + bDeletedOn : WordBool; { if above, deleted recs seen } + iRefRange : integer; { Not used } + exltMode : XLTMode; { Translate Mode } + iRestrVersion : integer; { Restructure version number } + bUniDirectional : WordBool; { Cursor is uni-directional } + Dummy4 : Word; + iFmlRights : integer; { Family rights } + iPasswords : integer; { Number of Aux passwords } + iCodePage : integer; { Codepage (0 if unknown) } + bProtected : WordBool; { Table is protected by password } + iTblLevel : integer; { Driver dependent table level } + szLangDriver : DBINAME; { Language driver name } + bFieldMap : WordBool; { Field map active } + iBlockSize : integer; { Physical file blocksize in K } + bStrictRefInt : WordBool; { Strict referential integrity } + iFilters : integer; { Number of filters } + bTempTable : WordBool; { Table is a temporary table } + iUnUsed : packed array [0..15] of Word; + end; + +//Delayed Update Types and Constants } + +type + DBIDelayedUpdCmd = ( { Op types for Delayed Update cursor } + dbiDelayedUpdCommit, { Commit the updates } + dbiDelayedUpdCancel, { Rollback the updates } + dbiDelayedUpdCancelCurrent, { Cancel the Current Rec Change } + dbiDelayedUpdPrepare { Phase1 of 2 phase commit } + ); + +//============================================================================// +// Record Properties // +//============================================================================// + +type + pRECProps = ^RECProps; + RECProps = packed record { Record properties } + iSeqNum : Longint; { When Seq# supported only } + iPhyRecNum : Longint; { When Phy Rec#s supported only } + iRecStatus : Word; { Delayed Updates Record Status } + bSeqNumChanged : WordBool; { Not used } + bDeleteFlag : WordBool; { When soft delete supported only } + end; + +//============================================================================// +// Index descriptor // +//============================================================================// + +type + pIDXDesc = ^IDXDesc; + IDXDesc = record { Index description } + szName : string; { Index name } + iIndexId : integer; { Index number } + szFormat : string; { Optional format (BTREE, HASH etc) } + bPrimary : WordBool; { True, if primary index } + bUnique : WordBool; { True, if unique keys (TRI-STATE for dBASE) } + bDescending : WordBool; { True, for descending index } + bMaintained : WordBool; { True, if maintained index } + bSubset : WordBool; { True, if subset index } + bExpIdx : WordBool; { True, if expression index } + iCost : integer; { Not used } + iFldsInKey : integer; { Fields in the key (1 for Exp) } + iKeyLen : integer; { Phy Key length in bytes (Key only) } + bOutofDate : WordBool; { True, if index out of date } + iKeyExpType : integer; { Key type of Expression } + aiKeyFld : DBIKEY; { Array of field numbers in key } + szKeyExp : string; { Key expression } + szKeyCond : string; { Subset condition } + bCaseInsensitive : WordBool; { True, if case insensitive index } + iBlockSize : integer; { Block size in bytes } + iRestrNum : integer; { Restructure number } + abDescending : packed array [0..DBIMAXFLDSINKEY-1] of WordBool; { TRUE } + iUnUsed : packed array [0..15] of Word; + end; + +//============================================================================// +// Table / Field Types // +//============================================================================// +const +{ Field Types (Logical) } + fldUNKNOWN = 0; + fldZSTRING = 1; { Null terminated string } + fldDATE = 2; { Date (32 bit) } + fldBLOB = 3; { Blob } + fldBOOL = 4; { Boolean (16 bit) } + fldINT16 = 5; { 16 bit signed number } + fldINT32 = 6; { 32 bit signed number } + fldFLOAT = 7; { 64 bit floating point } + fldBCD = 8; { BCD } + fldBYTES = 9; { Fixed number of bytes } + fldTIME = 10; { Time (32 bit) } + fldTIMESTAMP = 11; { Time-stamp (64 bit) } + fldUINT16 = 12; { Unsigned 16 bit integer } + fldUINT32 = 13; { Unsigned 32 bit integer } + fldFLOATIEEE = 14; { 80-bit IEEE float } + fldVARBYTES = 15; { Length prefixed var bytes } + fldLOCKINFO = 16; { Look for LOCKINFO typedef } + fldCURSOR = 17; { For Oracle Cursor type } + fldINT64 = 18; { 64 bit signed number } + fldUINT64 = 19; { Unsigned 64 bit integer } + fldADT = 20; { Abstract datatype (structure) } + fldARRAY = 21; { Array field type } + fldREF = 22; { Reference to ADT } + fldTABLE = 23; { Nested table (reference) } + {$IFDEF FPC} + fldDATETIME = 24; { DateTime structure field } + {$ENDIF} + {$IFDEF DELPHI_6} + fldDATETIME = 24; { DateTime structure field } + {$IFDEF DELPHI_12} + fldFMTBCD = 25; { BCD Variant type: required by Midas, same as BCD for DBExpress} + fldWIDESTRING = 26; { UCS2 null terminated string } + MAXLOGFLDTYPES = 27; { Number of logical fieldtypes } + {$ELSE} + MAXLOGFLDTYPES = 25; { Number of logical fieldtypes } + {$ENDIF} + {$ELSE} + MAXLOGFLDTYPES = 24; { Number of logical fieldtypes } + {$ENDIF} + + {$IFDEF DELPHI_12} + { Additional (non-BDE fieldtypes } + fldUNICODE = $1007; { Unicode } + {$ENDIF} + + //POSTGRES SPECIFIC + fldTIMESTAMPTZ = MAXLOGFLDTYPES + 1; + fldUUID = MAXLOGFLDTYPES + 2; + fldINET = MAXLOGFLDTYPES + 3; + fldMACADDR = MAXLOGFLDTYPES + 4; + fldPOINT = MAXLOGFLDTYPES + 5; + fldCIRCLE = MAXLOGFLDTYPES + 6; + fldBOX = MAXLOGFLDTYPES + 7; + fldLSEG = MAXLOGFLDTYPES + 8; + fldRANGE = MAXLOGFLDTYPES + 9; + +{ Sub Types (Logical) } + +{ fldFLOAT subtype } + + fldstMONEY = 21; { Money } + +{ fldBLOB subtypes } + + fldstMEMO = 22; { Text Memo } + fldstBINARY = 23; { Binary data } + fldstFMTMEMO = 24; { Formatted Text } + fldstOLEOBJ = 25; { OLE object (Paradox) } + fldstGRAPHIC = 26; { Graphics object } + fldstDBSOLEOBJ = 27; { dBASE OLE object } + fldstTYPEDBINARY = 28; { Typed Binary data } + fldstACCOLEOBJ = 30; { Access OLE object } + fldstHMEMO = 33; { CLOB } + fldstHBINARY = 34; { BLOB } + fldstBFILE = 36; { BFILE } + +{ fldZSTRING subtype } + + fldstPASSWORD = 1; { Password } + fldstFIXED = 31; { CHAR type } + fldstUNICODE = 32; { Unicode } + +{ fldINT32 subtype } + fldstAUTOINC = 29; + +{ fldADT subtype } + + fldstADTNestedTable = 35; { ADT for nested table (has no name) } + +{ fldDATE subtype } + fldstADTDATE = 37; { DATE (OCIDate ) with in an ADT } + +//============================================================================// +// Field descriptor // +//============================================================================// +type + FLDVchk = ( { Field Val Check type } + fldvNOCHECKS, { Does not have explicit val checks } + fldvHASCHECKS, { One or more val checks on the field } + fldvUNKNOWN { Dont know at this time } + ); + + FLDRights = ( { Field Rights } + fldrREADWRITE, { Field can be Read/Written } + fldrREADONLY, { Field is Read only } + fldrNONE, { No Rights on this field } + fldrUNKNOWN { Dont know at this time } + ); + + pFLDDesc = ^FLDDesc; + FLDDesc = packed record { Field Descriptor } + iFldNum : integer; { Field number (1..n) } + iNativeType : cardinal; { Field native type } + szName : string; { Field name } + iFldType : integer; { Field type } + iSubType : integer; { Field subtype (if applicable) } + iUnits1 : integer; { Number of Chars, digits etc } + iUnits2 : integer; { Decimal places etc. } + iOffset : integer; { Offset in the record (computed) } + iLen : integer; { Length in bytes (computed) } + iNullOffset : integer; { For Null bits (computed) } + efldvVchk : FLDVchk; { Field Has vcheck (computed) } + efldrRights : FLDRights; { Field Rights (computed) } + bCalcField : WordBool; { Is Calculated field (computed) } + iUnUsed : packed array [0..1] of integer; + end; + + TFLDDescList = array of FLDDesc; + + TIDXDescList = array of IDXDesc; + +//============================================================================// +// Validity check, Referential integrity descriptors // +//============================================================================// +// Subtypes for Lookup + LKUPType = ( { Paradox Lookup type } + lkupNONE, { Has no lookup } + lkupPRIVATE, { Just Current Field + Private } + lkupALLCORRESP, { All Corresponding + No Help } + lkupHELP, { Just Current Fld + Help and Fill } + lkupALLCORRESPHELP { All Corresponging + Help } + ); + + pVCHKDesc = ^VCHKDesc; + VCHKDesc = packed record { Val Check structure } + iFldNum : Word; { Field number } + bRequired : WordBool; { if True, value is required } + bHasMinVal : WordBool; { if True, has min value } + bHasMaxVal : WordBool; { if True, has max value } + bHasDefVal : WordBool; { if True, has default value } + aMinVal : DBIVCHK; { Min Value } + aMaxVal : DBIVCHK; { Max Value } + aDefVal : string; { Default value } + szPict : DBIPICT; { Picture string } + elkupType : LKUPType; { Lookup/Fill type } + szLkupTblName : string; { Lookup Table name } + end; + +//============================================================================// +// Miscellaneous // +//============================================================================// + +{ Index Id used to open table without a default index (i.e. no order) } +const + NODEFAULTINDEX = $FFFF; + + +//============================================================================// +// BookMark compares // +//============================================================================// + +type + PCMPBkMkRslt = ^CMPBkMkRslt; + CMPBkMkRslt = TypedEnum; +const + CMPLess = -1; { Bkm1 < Bkm2 } + CMPEql = 0; { BookMarks are exactly the same } + CMPGtr = 1; { Bkm1 > Bkm2 } + CMPKeyEql = 2; { Only Bkm1.key_val = Bkm2.key_val } + + +{============================================================================} +{ Key searches } +{============================================================================} + +type + DBISearchCond = ( { Search condition for keys } + keySEARCHEQ, { = } + keySEARCHGT, { > } + keySEARCHGEQ { >= } + ); + +//============================================================================// +// Filter description // +//============================================================================// + +type + pCANOp = ^CANOp; + CANOp = ( + canNOTDEFINED, { (*) } + canISBLANK, { CANUnary; is operand blank. (*) } + canNOTBLANK, { CANUnary; is operand not blank. (*) } + canEQ, { CANBinary, CANCompare; equal. (*) } + canNE, { CANBinary; NOT equal. (*) } + canGT, { CANBinary; greater than. (*) } + canLT, { CANBinary; less than. (*) } + canGE, { CANBinary; greater or equal. (*) } + canLE, { CANBinary; less or equal. (*) } + canNOT, { CANUnary; NOT (*) } + canAND, { CANBinary; AND (*) } + canOR, { CANBinary; OR (*) } + canTUPLE2, { CANUnary; Entire record is operand. } + canFIELD2, { CANUnary; operand is field (*) } + canCONST2, { CANUnary; operand is constant (*) } + canMINUS, { CANUnary; minus. } + canADD, { CANBinary; addition. } + canSUB, { CANBinary; subtraction. } + canMUL, { CANBinary; multiplication. } + canDIV, { CANBinary; division. } + canMOD, { CANBinary; modulo division. } + canREM, { CANBinary; remainder of division. } + canSUM, { CANBinary, accumulate sum of. } + canCOUNT, { CANBinary, accumulate count of. } + canMIN, { CANBinary, find minimum of. } + canMAX, { CANBinary, find maximum of. } + canAVG, { CANBinary, find average of. } + canCONT, { CANBinary; provides a link between two } + canUDF2, { CANBinary; invokes a User defined fn } + canCONTINUE2, { CANUnary; Stops evaluating records } + canLIKE, { CANCompare, extended binary compare (*) } + canIN, { CANBinary field in list of values } + canLIST2, { List of constant values of same type } + canUPPER, { CANUnary: upper case } + canLOWER, { CANUnary: lower case } + canFUNC2, { CANFunc: function } + canLISTELEM2, { CANListElem: List Element } + canASSIGN { CANBinary: Field assignment } + ); + + NODEClass = ( { Node Class } + nodeNULL, { Null node (*) } + nodeUNARY, { Node is a unary (*) } + nodeBINARY, { Node is a binary (*) } + nodeCOMPARE, { Node is a compare (*) } + nodeFIELD, { Node is a field (*) } + nodeCONST, { Node is a constant (*) } + nodeTUPLE, { Node is a record } + nodeCONTINUE, { Node is a continue node (*) } + nodeUDF, { Node is a UDF node } + nodeLIST, { Node is a LIST node } + nodeFUNC, { Node is a function node } + nodeLISTELEM { Node is a List Element node } + ); + +// NODE definitions including misc data structures // +//-------------------------------------------------// + +type + pCANHdr = ^CANHdr; + CANHdr = packed record { Header part common to all (*) } + nodeClass : NODEClass; + canOp : CANOp; + end; + + pCANUnary = ^CANUnary; + CANUnary = packed record { Unary Node (*) } + nodeClass : NODEClass; + canOp : CANOp; + iOperand1 : Word; { Byte offset of Operand node } + end; + + pCANBinary = ^CANBinary; + CANBinary = packed record { Binary Node (*) } + nodeClass : NODEClass; + canOp : CANOp; + iOperand1 : Word; { Byte offset of Op1 } + iOperand2 : Word; { Byte offset of Op2 } + end; + + pCANField = ^CANField; + CANField = packed record { Field } + nodeClass : NODEClass; + canOp : CANOp; + iFieldNum : Word; + iNameOffset : Word; { Name offset in Literal pool } + end; + + pCANConst = ^CANConst; + CANConst = packed record { Constant } + nodeClass : NODEClass; + canOp : CANOp; + iType : Word; { Constant type. } + iSize : Word; { Constant size. (in bytes) } + iOffset : Word; { Offset in the literal pool. } + end; + + pCANTuple = ^CANTuple; + CANTuple = packed record { Tuple (record) } + nodeClass : NODEClass; + canOp : CANOp; + iSize : Word; { Record size. (in bytes) } + end; + + pCANContinue = ^CANContinue; + CANContinue = packed record { Break Node (*) } + nodeClass : NODEClass; + canOp : CANOp; + iContOperand : Word; { Continue if operand is true. } + end; + + pCANCompare = ^CANCompare; + CANCompare = packed record { Extended compare Node (text fields) (*) } + nodeClass : NODEClass; + canOp : CANOp; { canLIKE, canEQ } + bCaseInsensitive : WordBool; { 3 val: UNKNOWN = "fastest", "native" } + iPartialLen : Word; { Partial fieldlength (0 is full length) } + iOperand1 : Word; { Byte offset of Op1 } + iOperand2 : Word; { Byte offset of Op2 } + end; + + pCANFunc = ^CANFunc; + CANFunc = packed record { function } + nodeClass : NODEClass; + canOp : CANOp; + iNameOffset : Word; { Name offset in Literal pool } + iElemOffset : Word; { Offset of first List Element in Node pool } + end; + + pCANListElem = ^CANListElem; + CANListElem = packed record { List Element } + nodeClass : NODEClass; + canOp : CANOp; + iOffset : Word; { Arg offset in Node pool } + iNextOffset : Word; { Offset in Node pool of next ListElem or 0 if end of list } + end; + + pCANList = ^CANList; + CANList = packed record { List of Constants } + nodeClass : NODEClass; + canOp : CANOp; + iType : Word; { Constant type. } + iTotalSize : Word; { Total list size; } + iElemSize : Word; { Size of each elem for fix-width types } + iElems : Word; { Number of elements in list } + iOffset : Word; { Offset in the literal pool to first elem. } + end; + + pCANNode = ^CANNode; + CANNode = packed record + case Integer of + 0: (canHdr : CANHdr); + 1: (canUnary : CANUnary); + 2: (canBinary : CANBinary); + 3: (canField : CANField); + 4: (canConst : CANConst); + 5: (canTuple : CANTuple); + 6: (canContinue : CANContinue); + 7: (canCompare : CANCompare); + 8: (canList : CANList); + 9: (canFunc : CANFunc); + 10: (canListElem : CANListElem); + end; + +type + ppCANExpr = ^pCANExpr; + pCANExpr = ^CANExpr; + CANExpr = packed record { Expression Tree } + iVer : Word; { Version tag of expression. } + iTotalSize : Word; { Size of this structure } + iNodes : Word; { Number of nodes } + iNodeStart : Word; { Starting offet of Nodes in this } + iLiteralStart : Word; { Starting offset of Literals in this } + end; + + pfGENFilter = function ( + ulClientData : Longint; + pRecBuf : Pointer; + iPhyRecNum : Longint + ): SmallInt; stdcall; + +//----------------------------------------------------------------------------// +// DBI Query related types // +//----------------------------------------------------------------------------// + + LIVENESS = ( + wantDEFAULT, { Default , same as wantCANNED } + wantLIVE, { Want live data even if extra effort (no guarantee) } + wantCANNED, { Want canned data even if extra effort (guaranteed) } + wantSPEED { Let query manager decide, find out afterwards } + ); + +{======================================================================} +{ Stored procedure and Stored procedure Param descriptor } +{======================================================================} + pSPParamDesc = ^SPParamDesc; + SPParamDesc = packed record + uParamNum : Word; + szName : string; + eParamType : STMTParamType; + uFldType : Word; + uSubType : Word; + iUnits1 : SmallInt; + iUnits2 : SmallInt; + uOffset : Word; + uLen : Word; + uNullOffset : Word; + end; + +//============================================================================// +// Call Backs // +//============================================================================// +type + pCBType = ^CBType; + CBType = ( { Call back type } + cbGENERAL, { General purpose } + cbRESERVED1, + cbRESERVED2, + cbINPUTREQ, { Input requested } + cbRESERVED4, + cbRESERVED5, + cbBATCHRESULT, { Batch processing rslts } + cbRESERVED7, + cbRESTRUCTURE, { Restructure } + cbRESERVED9, + cbRESERVED10, + cbRESERVED11, + cbRESERVED12, + cbRESERVED13, + cbRESERVED14, + cbRESERVED15, + cbRESERVED16, + cbRESERVED17, + cbTABLECHANGED, { Table changed notification } + cbRESERVED19, + cbCANCELQRY, { Allow user to cancel Query } + cbSERVERCALL, { Server Call } + cbRESERVED22, + cbGENPROGRESS, { Generic Progress report. } + cbDBASELOGIN, { dBASE Login } + cbDELAYEDUPD, { Delayed Updates } + cbFIELDRECALC, { Field(s) recalculation } + cbTRACE, { Trace } + cbDBLOGIN, { Database login } + cbDETACHNOTIFY, { DLL Detach Notification } + cbNBROFCBS { Number of cbs } + ); + +type + pCBRType = ^CBRType; + CBRType = ( { Call-back return type } + cbrUSEDEF, { Take default action } + cbrCONTINUE, { Continue } + cbrABORT, { Abort the operation } + cbrCHKINPUT, { Input given } + cbrYES, { Take requested action } + cbrNO, { Do not take requested action } + cbrPARTIALASSIST, { Assist in completing the job } + cbrSKIP, { Skip this operation } + cbrRETRY { Retry this operation } + ); + + ppfDBICallBack = ^pfDBICallBack; + pfDBICallBack = function ( { Call-back funtion pntr type } + ecbType : CBType; { Callback type } + iClientData : Longint; { Client callback data } + CbInfo : Pointer { Call back info/Client Input } + ): CBRType; stdcall; + + DelayUpdErrOpType = ( { type of delayed update object (delayed updates callback) } + delayupdNONE, + delayupdMODIFY, + delayupdINSERT, + delayupdDELETE + ); + + PDELAYUPDCbDesc = ^DELAYUPDCbDesc; + DELAYUPDCbDesc = packed record { delayed updates callback info } + iErrCode : DBIResult; + eDelayUpdOpType : DelayUpdErrOpType; + iRecBufSize : Word; { Record size (physical record) } + pNewRecBuf : Pointer; + pOldRecBuf : Pointer; + end; + + + +///////////////////////////////////////////////////////////////////////////// +// CONSTANTS DEFINITION // +///////////////////////////////////////////////////////////////////////////// +type + TFieldArray = array[0..255] of Integer; + TTrueArray = Set of AnsiDACByteChar; + TFalseArray = Set of AnsiDACByteChar; + +///////////////////////////////////////////////////////////////////////////// +// TPgSQLFilter TYPES AND CONST // +///////////////////////////////////////////////////////////////////////////// +type + TFldType = (FT_UNK, FT_INT, FT_DATETIME, FT_DATE, FT_TIME, FT_CURRENCY, FT_FLOAT, FT_STRING, FT_BOOL); + + StrRec = record + allocSiz : Longint; + refCnt : Longint; + length : Longint; + end; + +const + strsz = sizeof(StrRec); + +///////////////////////////////////////////////////////////////////////////// +// INDEX AND PRIMARY KEY DEFINITIONS // +///////////////////////////////////////////////////////////////////////////// +type + + TPropRec = Record + Prop : Word; + Group : Word; + end; + + TBlobItem = Record + Blob : TMemoryStream; + end; + + PPSQLBookMark = ^TPSQLBookMark; + TPSQLBookMark = Record + Position : Int64;//Longint; + end; + + PFieldStatus = ^TFieldStatus; + TFieldStatus = Record + isNULL : SmallInt; + Changed : LongBool; + end; + + TRecordState = (tsNoPos, tsPos, tsFirst, tsLast, tsEmpty, tsClosed); + TDir = (tdUndefined, tdNext, tdPrev); + + TSSLMode = (sslDisable , sslAllow, sslPrefer, sslRequire, sslVerifyCA, sslVerifyFull); + + TPSQLDatasetOption = (dsoByteaAsEscString, dsoOIDAsInt, dsoForceCreateFields, + dsoUseGUIDField, dsoTrimCharFields, dsoPopulateFieldsOrigin, + dsoManageLOFields, dsoEmptyCharAsNull, dsoUDTAsMaxString, + dsoRefreshModifiedRecordOnly, dsoFetchOnDemand, dsoNumericAsFloat); + + TPSQLDatasetOptions = set of TPSQLDatasetOption; + +const + SSLConsts: array[TSSLMode] of string = ('disable' , 'allow', 'prefer', + 'require', 'verify-ca', 'verify-full'); + + SSLOpts: array[0..3] of string = ('sslcert', 'sslkey', 'sslrootcert', 'sslcrl'); + +type + TDBOptions = record + User : String; + Password : String; + DatabaseName : String; + Port : Cardinal; + Host : String; + SSLMode : string; + ConnectionTimeout: cardinal; + end; + + + PPGFIELD_INFO = ^TPGFIELD_INFO; + TPGField_Info = record + FieldIndex : Integer; + FieldName : String; + FieldType : cardinal; + FieldSize : Integer; + FieldMaxSize : Integer; + FieldDefault : String; + FieldNotNull : boolean; + FieldTypMod : Integer; + end; + +///////////////////////////////////////////////////////////////////////////// +// BASE OBJECTS DEFINITIONS // +///////////////////////////////////////////////////////////////////////////// + {TContainer Object} + TContainer = Class(TObject) + private + FItems: TList{$IFDEF NEXTGEN}{$ENDIF}; + public + constructor Create; + Destructor Destroy; Override; + function At(Index: integer): Pointer; + procedure AtDelete(Index: integer); + procedure AtInsert(Index: integer; Item: Pointer); + procedure AtPut(Index: integer; Item: Pointer); + procedure Clear; + procedure Delete(Item: Pointer); + procedure DeleteAll; + procedure FreeAll; + function Get(AIndex: integer): Pointer; + function GetCount: integer; + function IndexOf(Item: Pointer): integer; + procedure Insert(Item: Pointer); Virtual; + procedure Pack; + procedure Put(AIndex: integer; APointer: Pointer); + function GetCapacity: integer; + procedure SetCapacity(NewCapacity: integer); + property Count: integer Read GetCount; + property Items[index: integer]: Pointer Read Get Write Put; + property Capacity: integer Read GetCapacity Write SetCapacity; + end; + + TBaseObject = Class(TObject) + Protected + FParent : TObject; + FContainer: TContainer; + Public + property Container : TContainer Read FContainer Write FContainer; + property Parent : TObject Read FParent Write FParent; + constructor Create(P : TObject; Container : TContainer); + Destructor Destroy; Override; + end; + +///////////////////////////////////////////////////////////////////////////// +// COMMON FUNCTIONS // +///////////////////////////////////////////////////////////////////////////// +{ SQL Parser } +type + TSQLToken = (stUnknown, stTableName, stFieldName, stAscending, stDescending, stSelect, + stFrom, stWhere, stGroupBy, stHaving, stUnion, stPlan, stOrderBy, stForUpdate, + stEnd, stPredicate, stValue, stIsNull, stIsNotNull, stLike, stAnd, stOr, + stNumber, stAllFields, stComment, stDistinct,stSubSelect,stFunction,stAliace,stAs); + +const + SQLSections = [stSelect, stFrom, stWhere, stGroupBy, stHaving, stUnion, + stPlan, stOrderBy, stForUpdate]; +{$IFDEF DELPHI_4} + curAUTOREFETCH = $00050017; { rw BOOL, Refetch inserted record } +{$ENDIF} + +function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; +function GetTable(const SQL: String; var Aliace : String): String; +function SqlDateToDateTime(Value: string; const IsTime: boolean): TDateTime; +function DateTimeToSqlDate(Value: TDateTime; Mode : integer): string; +function SQLTimeStampToDateTime(Value: string): TDateTime; +function StrToSQLFloat(Value: string): Double; +function SQLFloatToStr(Value: Double): string; + +procedure GetToken(var Buffer, Token: string); +procedure ConverPSQLtoDelphiFieldInfo(Info : TPGFIELD_INFO; Count, Offset : integer; + var RecBuff : FLDDesc; + var ValChk : VCHKDesc; + var LocArray : Boolean); + +procedure LoadPSQLLibrary(LibPQPath: string = ''); +procedure UnloadPSQLLibrary; +procedure CheckLibraryLoaded; +procedure HotLibrarySwitch(const ALibPath: string); +function IsLibraryLoaded: boolean; + +function IsValidIP(const S: string): boolean; + +function MaskSearch(const Str, Mask: string; + CaseSensitive : boolean = true; + MaskChar: Char = '?'; + WildCard: Char = '%'): Boolean; + +function Search(Op1,Op2 : Variant; OEM, CaseSen : Boolean; PartLen: Integer):Boolean; +function GetBDEErrorMessage(ErrorCode : Word):String; +procedure FieldMapping(FieldType : cardinal; phSize : Integer; var BdeType : integer; + var BdeSubType : integer; var LogSize : Integer; + var LocArray : Boolean); + +{$IFNDEF DELPHI_16} +function UIntToStr(C: cardinal): string; +{$ENDIF} +function StrToUInt(S: string): cardinal; +function StrToUIntDef(S: string; DefVal: cardinal = 0): cardinal; + +function DeBracketedStr(const Value: string; ALeftBracket: char = '('; ARightBracket: char = ')'): string; + +{$IFNDEF DELPHI_12} +type + TCharSet = set of char; + TRecordBuffer = PAnsiChar; + + function CharInSet(C: Char; const CharSet: TCharSet): Boolean; +{$ENDIF} + +{$IFDEF NEXTGEN} +type + TRecordBuffer = PByte; +{$ENDIF NEXTGEN} + +{$IFNDEF DELPHI_12} + + {$IFDEF DELPHI_5} + function Utf8Encode(const WS: WideString): AnsiString; + {$ENDIF} + + function UTF8ToString(const S: String): string; +{$ENDIF} + +{$IFDEF DELPHI_5} +function GetModuleName(Module: HMODULE): string; +function Sign(const AValue: Double): integer; +{$ENDIF} + + +//function for compatibility with FreePascal and MacOS +{$IFNDEF MSWINDOWS} +procedure ZeroMemory(Destination: Pointer; Length: integer); +procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: integer); +{$ENDIF MSWINDOWS} +{$IFDEF MACOS} +function GetTickCount: LongWord; //thanks to Indy project +{$ELSE} +function GetTickDiff(const AOldTickCount, ANewTickCount: LongWord): LongWord; +{$ENDIF} + +function DACAnsiStrAlloc(Size: Cardinal): PAnsiDACChar; +procedure DACAnsiStrDispose(Str: PAnsiDACChar); + +function DACStrCopy(Dest: PAnsiDACChar; const Source: PAnsiDACChar; MaxLen: Cardinal = 0): PAnsiDACChar; +procedure DACAllocStr(var Dest: PAnsiDACChar; Len: Integer); + +{$IFDEF MOBILE} +function DACAnsiStrBufSize(const Str: PAnsiDACChar): Cardinal; +function DACStrBufSize(const Str: PAnsiDACChar): Cardinal; +{$ENDIF} + +function GetTickCount: LongWord; //thanks to Indy project + + +implementation + +uses PSQLDbTables, PSQLAccess + {$IFDEF DELPHI_6}, StrUtils{$ENDIF} + {$IFDEF FPC}, StrUtils{$ENDIF} + {$IFDEF NEXTGEN}, Character{$ENDIF}; + +type + T4 = 0..3; + T8 = 0..7; + TIPv4ByteArray = array[T4] of Byte; + TIPv6WordArray = array[T8] of Word; + + TIPv4 = packed record + case Integer of + 0: (D, C, B, A: Byte); + 1: (Groups: TIPv4ByteArray); + 2: (Value: Cardinal); + end; + + TIPv6 = packed record + case Integer of + 0: (H, G, F, E, D, C, B, A: Word); + 1: (Groups: TIPv6WordArray); + end; + +{$IFDEF UNDER_DELPHI_6} +function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer; +var + I,X: Integer; + Len, LenSubStr: Integer; +begin + if Offset = 1 then + Result := Pos(SubStr, S) + else + begin + I := Offset; + LenSubStr := Length(SubStr); + Len := Length(S) - LenSubStr + 1; + while I <= Len do + begin + if S[I] = SubStr[1] then + begin + X := 1; + while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do + Inc(X); + if (X = LenSubStr) then + begin + Result := I; + exit; + end; + end; + Inc(I); + end; + Result := 0; + end; +end; + +function TryStrToInt(const S: string; out V: integer): boolean; +var Code: integer; +begin + Val(S, V, Code); + Result := Code = 0; +end; +{$ENDIF UNDER_DELPHI_6} + +function TryStrToIPv4(const S: String; out Value: TIPv4): boolean; +var + SIP: String; + Start: Integer; + I: T4; + Index: Integer; + Count: Integer; + SGroup: String; + G: Integer; +begin + Result := False; + SIP := S + '.'; + Start := 1; + for I := High(T4) downto Low(T4) do + begin + Index := PosEx('.', SIP, Start); + if Index = 0 then + Exit; + Count := Index - Start + 1; + SGroup := Copy(SIP, Start, Count - 1); + if TryStrToInt(SGroup, G) and (G >= Low(Byte)) and (G < High(Byte)) then + Value.Groups[I] := G + else + Exit; + Inc(Start, Count); + end; + Result := True; +end; + +function TryStrToIPv6(const S: String; out Value: TIPv6): boolean; +{ Valid examples for S: + 2001:0db8:85a3:0000:0000:8a2e:0370:7334 + 2001:db8:85a3:0:0:8a2e:370:7334 + 2001:db8:85a3::8a2e:370:7334 + ::8a2e:370:7334 + 2001:db8:85a3:: + ::1 + :: + ::ffff:c000:280 + ::ffff:192.0.2.128 } +var + ZeroPos: Integer; + DotPos: Integer; + SIP: String; + Start: Integer; + Index: Integer; + Count: Integer; + SGroup: String; + G: Integer; + + procedure NormalNotation; + var + I: T8; + begin + SIP := S + ':'; + Start := 1; + for I := High(T8) downto Low(T8) do + begin + Index := PosEx(':', SIP, Start); + if Index = 0 then + Exit; + Count := Index - Start + 1; + SGroup := '$' + Copy(SIP, Start, Count - 1); + if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then + Exit; + Value.Groups[I] := G; + Inc(Start, Count); + end; + Result := True; + end; + + procedure CompressedNotation; + var + I: T8; + A: array of Word; + begin + SIP := S + ':'; + Start := 1; + I := High(T8); + while Start < ZeroPos do + begin + Index := PosEx(':', SIP, Start); + if Index = 0 then + Exit; + Count := Index - Start + 1; + SGroup := '$' + Copy(SIP, Start, Count - 1); + if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then + Exit; + Value.Groups[I] := G; + Inc(Start, Count); + Dec(I); + end; + FillChar(Value.H, (I + 1) * SizeOf(Word), 0); + if ZeroPos < (Length(S) - 1) then + begin + SetLength(A, I + 1); + Start := ZeroPos + 2; + repeat + Index := PosEx(':', SIP, Start); + if Index > 0 then + begin + Count := Index - Start + 1; + SGroup := '$' + Copy(SIP, Start, Count - 1); + if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then + Exit; + A[I] := G; + Inc(Start, Count); + Dec(I); + end; + until Index = 0; + Inc(I); + Count := Length(A) - I; + Move(A[I], Value.H, Count * SizeOf(Word)); + end; + Result := True; + end; + + procedure DottedQuadNotation; + var + I: T4; + begin + if UpperCase(Copy(S, ZeroPos + 2, 4)) <> 'FFFF' then + Exit; + FillChar(Value.E, 5 * SizeOf(Word), 0); + Value.F := $FFFF; + SIP := S + '.'; + Start := ZeroPos + 7; + for I := Low(T4) to High(T4) do + begin + Index := PosEx('.', SIP, Start); + if Index = 0 then + Exit; + Count := Index - Start + 1; + SGroup := Copy(SIP, Start, Count - 1); + if not TryStrToInt(SGroup, G) or (G > High(Byte)) or (G < 0) then + Exit; + case I of + 0: Value.G := G shl 8; + 1: Inc(Value.G, G); + 2: Value.H := G shl 8; + 3: Inc(Value.H, G); + end; + Inc(Start, Count); + end; + Result := True; + end; + +begin + Result := False; + ZeroPos := Pos('::', S); + if ZeroPos = 0 then + NormalNotation + else + begin + DotPos := Pos('.', S); + if DotPos = 0 then + CompressedNotation + else + DottedQuadNotation; + end; +end; + +function IsValidIP(const S: string): boolean; +var IP4: TIPv4; + IP6: TIPv6; +begin + Result := TryStrToIPv4(S, IP4) or TryStrToIPv6(S, IP6); +end; + +{$IFNDEF MSWINDOWS} +procedure ZeroMemory(Destination: Pointer; Length: integer); +begin + FillChar(Destination^, Length, 0); +end; + +procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: integer); +begin + Move(Source^, Destination^, Length); +end; +{$ENDIF MSWINDOWS} + +{$IFDEF MACOS} +function GetTickCount: LongWord; inline; +begin + Result := AbsoluteToNanoseconds(UpTime) div 1000000; +end; +{$ENDIF} + +function GetTickDiff(const AOldTickCount, ANewTickCount: LongWord): LongWord; +{$IFDEF DELPHI_12}inline;{$ENDIF} +begin + {This is just in case the TickCount rolled back to zero} + if ANewTickCount >= AOldTickCount then begin + Result := ANewTickCount - AOldTickCount; + end else begin + Result := High(LongWord) - AOldTickCount + ANewTickCount; + end; +end; + +{$IFDEF DELPHI_5} +function GetModuleName(Module: HMODULE): string; +var + ModName: array[0..MAX_PATH] of Char; +begin + SetString(Result, ModName, GetModuleFileName(Module, ModName, SizeOf(ModName))); +end; + +function Sign(const AValue: Double): integer; +begin + if ((PInt64(@AValue)^ and $7FFFFFFFFFFFFFFF) = $0000000000000000) then + Result := 0 + else if ((PInt64(@AValue)^ and $8000000000000000) = $8000000000000000) then + Result := -1 + else + Result := 1; +end; +{$ENDIF} + +{$IFNDEF DELPHI_12} + + {$IFDEF DELPHI_5} + function Utf8Encode(const WS: WideString): AnsiString; + begin + Result := WS; + end; + {$ENDIF} + + function UTF8ToString(const S: String): string; + begin + {$IFDEF DELPHI_5} + Result := S; + {$ELSE} + Result := Utf8Decode(S); + {$ENDIF} + end; + +{$ENDIF} + +{$IFNDEF DELPHI_12} +function CharInSet(C: Char; const CharSet: TCharSet): Boolean; +begin + Result := C in CharSet; +end; +{$ENDIF} + +constructor TContainer.Create; +begin + Inherited Create; + FItems := TList{$IFDEF NEXTGEN}{$ENDIF}.Create; +end; + +Destructor TContainer.Destroy; +begin + FreeAll; + FItems.Free; + Inherited Destroy; +end; + +function TContainer.At(Index : integer) : Pointer; +begin + Try + Result := FItems[Index]; + Except + On E:EListError do Result := nil; + end; +end; + +procedure TContainer.AtDelete(Index : integer); +begin + FItems.Delete(Index); +end; + +procedure TContainer.AtInsert( Index : integer; Item : pointer ); +begin + FItems.Insert( Index, Item ); +end; + +procedure TContainer.AtPut( Index : integer; Item : pointer ); +begin + FItems[ Index ] := Item; +end; + +procedure TContainer.Clear; +begin + FItems.Clear; +end; + +procedure TContainer.Delete( Item : pointer ); +var + i : Integer; +begin + i := IndexOf( Item ); + if i <> -1 then AtDelete(i); +end; + +procedure TContainer.DeleteAll; +begin + FItems.Clear; +end; + +procedure TContainer.FreeAll; +var + I : integer; +begin + Try + for I := Count -1 downto 0 do + TObject(At(I)).Free; + Except + On EListError do ; + End; + FItems.Clear; +end; + +function TContainer.Get(AIndex : integer) : pointer; +begin + Result := FItems[AIndex]; +end; + +function TContainer.GetCount: integer; +begin + Result := FItems.Count; +end; + +function TContainer.IndexOf( Item : pointer ) : integer; +begin + Result := FItems.IndexOf( Item ); +end; + +procedure TContainer.Insert(Item : pointer); +begin + FItems.Add(Item); +end; + +procedure TContainer.Pack; +begin + FItems.Pack; +end; + +procedure TContainer.Put( AIndex : integer; APointer : pointer ); +begin + FItems[AIndex] := APointer; +end; + +function TContainer.GetCapacity : Integer; +begin + Result := FItems.Capacity; +end; + +procedure TContainer.SetCapacity( NewCapacity : Integer ); +begin + FItems.Capacity := NewCapacity; +end; + +///////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION TBASEOBJECT OBJECT // +///////////////////////////////////////////////////////////////////////////// +constructor TBaseObject.Create(P : TObject; Container : TContainer); +begin + Inherited Create; + FParent := P; + FContainer := Container; + if FContainer <> nil then FContainer.Insert(Self); +end; + +Destructor TBaseObject.Destroy; +begin + if FContainer <> nil then FContainer.Delete(Self); + Inherited Destroy; +end; + +///////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION COMMON FUNCTIONS // +///////////////////////////////////////////////////////////////////////////// +{ SQL Parser } +function NextSQLToken(var p: PChar; out Token: string; CurSection: TSQLToken): TSQLToken; +var + DotStart: Boolean; + + function NextTokenIs(Value: string; var Str: string): Boolean; + var + Tmp: PChar; + S: string; + begin + Tmp := p; + NextSQLToken(Tmp, S, CurSection); + Result := AnsiCompareText(Value, S) = 0; + if Result then + begin + Str := Str + ' ' + S; + p := Tmp; + end; + end; + + function GetSQLToken(var Str: string): TSQLToken; + var + l: PChar; + s: string; + begin + if Length(Str) = 0 then + Result := stEnd else + if (Str = '*') and (CurSection = stSelect) then + Result := stAllFields else + if DotStart then + Result := stFieldName else + if (AnsiCompareText('DISTINCT', Str) = 0) and (CurSection = stSelect) then + Result := stDistinct else + if (AnsiCompareText('ASC', Str) = 0) or (AnsiCompareText('ASCENDING', Str) = 0)then + Result := stAscending else + if (AnsiCompareText('DESC', Str) = 0) or (AnsiCompareText('DESCENDING', Str) = 0)then + Result := stDescending else + if (AnsiCompareText('SELECT', Str) = 0) and (CurSection = stUnknown) then + Result := stSelect else + if (AnsiCompareText('SELECT', Str) = 0) and (CurSection in [stSelect, stFieldName]) then + Result := stSubSelect else + if AnsiCompareText('AND', Str) = 0 then + Result := stAnd else + if AnsiCompareText('OR', Str) = 0 then + Result := stOr else + if AnsiCompareText('LIKE', Str) = 0 then + Result := stLike else + if (AnsiCompareText('AS', Str) = 0) then + Result := stAs else + if (AnsiCompareText('IS', Str) = 0) then + begin + if NextTokenIs('NULL', Str) then + Result := stIsNull else + begin + l := p; + s := Str; + if NextTokenIs('NOT', Str) and NextTokenIs('NULL', Str) then + Result := stIsNotNull else + begin + p := l; + Str := s; + Result := stValue; + end; + end; + end else + if AnsiCompareText('FROM', Str) = 0 then + Result := stFrom else + if AnsiCompareText('WHERE', Str) = 0 then + Result := stWhere else + if (AnsiCompareText('GROUP', Str) = 0) and NextTokenIs('BY', Str) then + Result := stGroupBy else + if AnsiCompareText('HAVING', Str) = 0 then + Result := stHaving else + if AnsiCompareText('UNION', Str) = 0 then + Result := stUnion else + if AnsiCompareText('PLAN', Str) = 0 then + Result := stPlan else + if (AnsiCompareText('FOR', Str) = 0) and NextTokenIs('UPDATE', Str) then + Result := stForUpdate else + if (AnsiCompareText('ORDER', Str) = 0) and NextTokenIs('BY', Str) then + Result := stOrderBy else + if AnsiCompareText('NULL', Str) = 0 then + Result := stValue else + if AnsiCompareText('SUBSTRING', Str) = 0 then + Result := stFunction else + if AnsiCompareText('TRIM', Str) = 0 then + Result := stFunction else + if CurSection = stFrom then + Result := stTableName else + if (CurSection = stTableName) or (CurSection = stAs) then + Result := stAliace else + Result := stFieldName; + + end; + +var + TokenStart: PChar; + + procedure StartToken; + begin + if not Assigned(TokenStart) then + TokenStart := p; + end; + +var + Literal: Char; + Mark: PChar; + BracketCount : integer; + LoopEnd: boolean; +begin + TokenStart := nil; + DotStart := False; + while True do + begin + case p^ of + '"','''','`': + begin + StartToken; + Literal := p^; + Mark := p; + //changed by pasha_golub 29.12.04, to deal with schema names + repeat + Inc(p); + LoopEnd := (p^ = Literal) or (p^ = #0); + if LoopEnd and (p^ <> #0) then + begin + inc(p); + if p^ = '.' then + begin + inc(p,2); + LoopEnd := False; + end; + end; + until LoopEnd; + if p^ = #0 then + begin + p := Mark; + Inc(p); + end else + begin + Inc(p); + SetString(Token, TokenStart, p - TokenStart); + Token := Trim(Token); + if DotStart then + Result := stFieldName else + if p^ = '.' then + Result := stTableName else + Result := stValue; + Exit; + end; + end; + '/': + begin + StartToken; + Inc(p); + if (p^ = '/') or (p^ = '*') then + begin + if p^ = '*' then + begin + repeat Inc(p) until (p = #0) or ((p^ = '*') and (p[1] = '/')); + end else + while (p^ <> #0) and (p^ <> #10) and (p^ <> #13) do Inc(p); + SetString(Token, TokenStart, p - TokenStart); + Result := stComment; + Exit; + end; + end; + ' ', #10, #13, ',', '(' ,')': + begin + if Assigned(TokenStart) then + begin + SetString(Token, TokenStart, p - TokenStart); + Result := GetSQLToken(Token); + if Result = stSubSelect then + repeat Inc(p) until (p^ = ')') else + Exit; + end else + begin + {$IFNDEF NEXTGEN} + if not CharInSet(p^, ['(',')']) then + while CharInSet(p^, [' ', #10, #13, ',']) do Inc(p) else + {$ELSE} + if not p^.IsInArray(['(',')']) then + while p^.IsInArray([' ', #10, #13, ',']) do Inc(p) else + {$ENDIF} + begin + BracketCount := 1; + repeat + Inc(p); + if p^ = '(' then Inc(BracketCount); + if p^ = ')' then Dec(BracketCount); + until (BracketCount = 0) or (p^ = #0) {safety measure}; + Inc(p); + end; + end; + + end; + '.': + begin + if Assigned(TokenStart) then + begin + SetString(Token, TokenStart, p - TokenStart); + Result := stTableName; + Exit; + end else + begin + DotStart := True; + Inc(p); + end; + end; + '=','<','>': + begin + if not Assigned(TokenStart) then + begin + TokenStart := p; + {$IFNDEF NEXTGEN} + while CharInSet(p^, ['=','<','>']) do Inc(p); + {$ELSE} + while p^.IsInArray(['=','<','>']) do Inc(p); + {$ENDIF} + SetString(Token, TokenStart, p - TokenStart); + Result := stPredicate; + Exit; + end; + Inc(p); + end; + '0'..'9': + begin + if not Assigned(TokenStart) then + begin + TokenStart := p; + {$IFNDEF NEXTGEN} + while CharInSet(p^, ['0'..'9','.']) do Inc(p); + {$ELSE} + while p^.IsInArray(['0', '1', '2','3','4','5','6','7','8','9','.']) do Inc(p); + {$ENDIF} + SetString(Token, TokenStart, p - TokenStart); + Result := stNumber; + Exit; + end else + Inc(p); + end; + #0: + begin + if Assigned(TokenStart) then + begin + SetString(Token, TokenStart, p - TokenStart); + Result := GetSQLToken(Token); + Exit; + end else + begin + Result := stEnd; + Token := ''; + Exit; + end; + end; + else + StartToken; + Inc(p); + end; + end; +end; + +function GetTable(const SQL: String; var Aliace : String): string; +var + Start: PChar; + Token: string; + SQLToken, CurSection: TSQLToken; +begin + Result := ''; + Start := PChar(SQL); + CurSection := stUnknown; + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then CurSection := SQLToken; + until SQLToken in [stEnd, stFrom]; + if SQLToken = stFrom then + begin + repeat + SQLToken := NextSQLToken(Start, Token, CurSection); + if SQLToken in SQLSections then + CurSection := SQLToken else + if (SQLToken = stTableName) or (SQLToken = stValue) then + begin + Result := Token; + Aliace := ''; + if Start[0] = '.' then + begin + CurSection := SQLToken; + SQLToken := NextSQLToken(Start, Token, CurSection); + if (SQLToken = stFieldName) or (SQLToken = stValue) + then Result := Result + '.' + Token; + end; + + while (Start[0] = ' ') and not (SQLToken in [stEnd]) do + begin + CurSection := SQLToken; + SQLToken := NextSqlToken(Start, Token, CurSection); + if SQLToken = stAliace then + Aliace := Token; + end; + Exit; + end; + until (CurSection <> stFrom) or (SQLToken in [stEnd, stTableName]); + end; +end; + +function SqlDateToDateTime(Value: string; const IsTime: boolean): TDateTime; +var + Year, Month, Day, Hour, Min, Sec, MSec: Integer; + Temp: string; +begin + Temp := Value; + try + if not IsTime then + begin + Year := StrToIntDef(Copy(Temp,1,4),1); + Month := StrToIntDef(Copy(Temp,6,2),1); + Day := StrToIntDef(Copy(Temp,9,2),1); + Result := EncodeDate(Year, Month, Day); + end + else + begin + Hour := StrToIntDef(Copy(Temp,1,2),0); + Min := StrToIntDef(Copy(Temp,4,2),0); + Sec := StrToIntDef(Copy(Temp,7,2),0); + MSec := StrToIntDef(Copy(Copy(Temp,10,3) + '000',1,3),0); //19.05.2008: for cases when trailing 0 are missing + Result := EncodeTime(Hour, Min, Sec, Msec); + end; + except + Result := 0; + end; +end; + +function DateTimeToSqlDate(Value: TDateTime; Mode: Integer): string; +begin + Result := ''; + case Mode of + TIMESTAMP_MODE: Result := FormatDateTime('mm-dd-yyyy hh:nn:ss.zzz', Value, PSQL_FS); + DATE_MODE: Result := FormatDateTime('mm-dd-yyyy', Value, PSQL_FS); + TIME_MODE: Result := FormatDateTime('hh:nn:ss.zzz', Value, PSQL_FS); + end; +end; + +function SQLTimestampToDateTime(Value: string): TDateTime; +var + Year, Month, Day, Hour, Min, Sec, MSec, Idx: Integer; +{$IFDEF DELPHI_5} +const + MinDateTime: TDateTime = -657434.0; { 01/01/0100 12:00:00.000 AM } + MaxDateTime: TDateTime = 2958465.99999; { 12/31/9999 11:59:59.999 PM } +{$ENDIF} +begin + if value = 'infinity' then + Result := MaxDateTime //EncodeDate(9999, 12, 31) + EncodeTime(0, 0, 0, 0) + else + if value = '-infinity' then + Result := MinDateTime //EncodeDate(0, 1, 1) + EncodeTime(0, 0, 0, 0) + else + begin + for Idx := Length(Value) downto Length(Value) - TIMEZONELEN do //crop timezone information "+\-dd:dd" + {$IFNDEF NEXTGEN} + if CharInSet(Value[Idx], ['+', '-']) then + {$ELSE} + if Value[Idx].IsInArray(['+', '-']) then + {$ENDIF} + begin + Value := Copy(Value, 1, Idx - 1); + Break; + end; + Year := Max(1, StrToIntDef(Copy(Value, 1, 4), 1)); + Month := Max(1, StrToIntDef(Copy(Value, 6, 2), 1)); + Day := Max(1, StrToIntDef(Copy(Value, 9, 2), 1)); + Hour := StrToIntDef(Copy(Value, 12, 2), 0); + Min := StrToIntDef(Copy(Value, 15, 2), 0); + Sec := StrToIntDef(Copy(Value, 18, 2), 0); + Msec := StrToIntDef(Copy(Copy(Value, 21, 3) + '000', 1, 3), 0); //19.05.2008: for cases when trailing 0 are missing + Result := EncodeDate(Year, Month, Day); + if Result >= 0 then + Result := Result + EncodeTime(Hour, Min, Sec, MSec) + else + Result := Result - EncodeTime(Hour, Min, Sec, MSec); + end; +end; + +function StrToSQLFloat(Value: string): Double; +begin + if Value <> '' then + try + Result := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}StrToFloat(Value, PSQL_FS); + except + Result := 0; + end + else + Result := 0; +end; + +function SQLFloatToStr(Value: Double): string; +begin + Result := {$IFDEF UNDER_DELPHI_6}PSQLAccess.{$ENDIF}FloatToStr(Value, PSQL_FS); +end; + + +procedure GetToken(var Buffer, Token: string); +label ExitProc; +var + P: Integer; + Quote: string; +begin + P := {$IFNDEF NEXTGEN}1{$ELSE}0{$ENDIF}; + Token := ''; + if Buffer = '' then Exit; + {$IFNDEF NEXTGEN} + while CharInSet(Buffer[P], [' ',#9]) do + {$ELSE} + while Buffer[P].IsInArray([' ',#9]) do + {$ENDIF} + begin + Inc(P); + if Length(Buffer) < P then goto ExitProc; + end; + if (Pos(Buffer[P],DELIMITERS) <> 0) then + begin + Token := Buffer[P]; + Inc(P); + goto ExitProc; + end; + {$IFNDEF NEXTGEN} + if CharInSet(Buffer[P], ['"','''']) then + {$ELSE} + if Buffer[P].IsInArray(['"','''']) then + {$ENDIF} + begin + Quote := Buffer[P]; + Token := Quote; + Inc(P); + while P <= Length(Buffer) do + begin + Token := Token + Buffer[P]; + Inc(P); + if (Buffer[P-1] = Quote) and (Buffer[P-2] <> '\') then Break; + end; + end else + begin + while P <= Length(Buffer) do + begin + Token := Token + Buffer[P]; + Inc(P); + if (P > Length(Buffer)) or (Pos(Buffer[P],DELIMITERS) <> 0) + {$IFNDEF NEXTGEN} + or CharInSet(Buffer[P], ['"','''']) then Break; + {$ELSE} + or Buffer[P].IsInArray(['"','''']) then Break; + {$ENDIF} + end; + end; +ExitProc: + Delete(Buffer, 1, {$IFNDEF NEXTGEN}P-1{$ELSE}P{$ENDIF}); +end; + +function GetInArrayField(FieldType : Word): boolean; +var + I : Integer; +begin + Result := False; + for i := 0 to MAXARRFLDTYPES-1 do + begin + if FieldType = FldArrayType[I] then + begin + Result := True; + Break; + end; + end; +end; + +{$IFNDEF DELPHI_17} +function UIntToStr(C: cardinal): string; +begin + Result := IntToStr(C); +end; +{$ENDIF} + +function StrToUInt(S: string): cardinal; +var E: integer; +begin + Val(S, Result, E); + if E <> 0 then raise EConvertError.Create(S + ' is not valid cardinal value'); +end; + +function StrToUIntDef(S: string; DefVal: cardinal = 0): cardinal; +var R: int64; +begin + R := StrToInt64Def(S, DefVal); + if (R >= Low(Cardinal)) and (R <= High(Cardinal)) then + Result := R + else + Result := DefVal; +end; + +function DeBracketedStr(const Value: string; ALeftBracket, ARightBracket: char): string; +var I, L: integer; +begin + L := length(Value); + for I := 1 to L div 2 do + if (Value[i] <> ALeftBracket) OR (Value[L - i + 1] <> ARightBracket) then + Break; + Result := Copy(Value, I, L - 2 * (I - 1)); +end; + +procedure FieldMapping(FieldType : cardinal; phSize : Integer; var BdeType : integer; + var BdeSubType : integer; var LogSize : Integer; + var LocArray : Boolean); +begin + BdeType := fldUNKNOWN; + BdeSubType := 0; + LogSize := 0; + LocArray := GetInArrayField(FieldType); + Case FieldType of + FIELD_TYPE_BOOL: begin + BdeType := fldBOOL; + LogSize := SizeOf(SmallInt); + end; + FIELD_TYPE_BPCHAR, + FIELD_TYPE_CHAR, + FIELD_TYPE_VARCHAR, + FIELD_TYPE_TINTERVAL: + begin + BdeType := fldZSTRING; + LogSize := phSize + 1; + if FieldType = FIELD_TYPE_BPCHAR then + BdeSubType := fldstFIXED; + end; + FIELD_TYPE_OID, + FIELD_TYPE_BYTEA: begin + BdeType := fldBLOB; + LogSize := SizeOf(TBlobItem); + end; + FIELD_TYPE_TEXT: begin + BdeType := fldBLOB; + LogSize := SizeOf(TBlobItem); + BdeSubType := fldstMemo; + end; + FIELD_TYPE_INT2: begin + BDEType := fldINT16; + LogSize := Sizeof(SmallInt); + end; + FIELD_TYPE_INT4: begin + BDEType := fldINT32; + LogSize := Sizeof(LongInt); + end; + FIELD_TYPE_INT8: begin + BDEType := fldINT64; + LogSize := Sizeof(Int64); + end; + FIELD_TYPE_DATE: begin + BdeType := fldDATE; + LogSize := Sizeof(TTimeStamp); + end; + FIELD_TYPE_TIME: begin + BdeType := fldTIME; + LogSize := Sizeof(TDateTime); + end; + + FIELD_TYPE_TIMESTAMP: + begin + BdeType := fldTIMESTAMP; + LogSize := SizeOf(TDateTime); + end; + FIELD_TYPE_TIMESTAMPTZ: begin + BdeType := fldZSTRING; + LogSize := TIMESTAMPTZLEN + 1; + end; +{$IFDEF UNDER_DELPHI_12} + FIELD_TYPE_NUMERIC, +{$ENDIF} + FIELD_TYPE_FLOAT4, + FIELD_TYPE_FLOAT8: + begin + BdeType := fldFLOAT; + LogSize := Sizeof(Double); + end; +{$IFDEF DELPHI_12} + FIELD_TYPE_NUMERIC: begin + BdeType := fldFMTBCD; + LogSize := phSize; + end; +{$ENDIF} + FIELD_TYPE_MONEY: begin + BdeType := fldZSTRING; + //BdeSubType := fldstMONEY; + LogSize := 32; //Sizeof(Single); + end; + FIELD_TYPE_NAME: begin + BdeType := fldZSTRING; + LogSize := NAMEDATALEN + 1; + end; + FIELD_TYPE_TIMETZ: begin + BdeType := fldZSTRING; + LogSize := TIMETZLEN + 1; + end; + FIELD_TYPE_BIT: begin + BdeType := fldZSTRING; + LogSize := phSize + 1; + end; + FIELD_TYPE_UUID: begin + BdeType := fldUUID; + LogSize := UUIDLEN + 1; + end; + FIELD_TYPE_INET, + FIELD_TYPE_CIDR: begin + BdeType := fldZSTRING; + LogSize := INETLEN + 1; + end; + FIELD_TYPE_MACADDR: begin + BdeType := fldZSTRING; + LogSize := MACADDRLEN + 1; + end; +{$IFDEF DELPHI_12} + FIELD_TYPE_POINT: + begin + BdeType := fldPOINT; + LogSize := SizeOf(TPSQLPoint); + end; + FIELD_TYPE_CIRCLE: + begin + BdeType := fldCIRCLE; + LogSize := SizeOf(TPSQLCircle); + end; + FIELD_TYPE_BOX: + begin + BdeType := fldBOX; + LogSize := SizeOf(TPSQLBox); + end; + FIELD_TYPE_LSEG: + begin + BdeType := fldLSEG; + LogSize := SizeOf(TPSQLLSeg); + end; + FIELD_TYPE_NUMRANGE, + FIELD_TYPE_DATERANGE, + FIELD_TYPE_INT4RANGE, + FIELD_TYPE_INT8RANGE, + FIELD_TYPE_TSRANGE, + FIELD_TYPE_TSTZRANGE: + begin + BdeType := fldRANGE; + LogSize := SizeOf(TPSQLRange); + end +{$ENDIF DELPHI_12} + else + begin + BdeType := fldZSTRING; + LogSize := phSize+1; + end; + end; +end; + +procedure ConverPSQLtoDelphiFieldInfo(Info : TPGFIELD_INFO; + Count, Offset : integer; + var RecBuff : FLDDesc; + var ValChk : VCHKDesc; + var LocArray : Boolean); +var + LogSize : Integer; + dataLen : Integer; +begin + {$IFNDEF NEXTGEN} + ZeroMemory(@RecBuff, Sizeof(FLDDesc)); + ZeroMemory(@ValChk, SizeOf(VCHKDesc)); + {$ELSE} + FillChar(RecBuff, Sizeof(FLDDesc), 0); + FillChar(ValChk, SizeOf(VCHKDesc), 0); + {$ENDIF} + with RecBuff do + begin + iFldNum := Count; + iNativeType := Info.FieldType; + ValChk.iFldNum := Count; + DataLen := Info.FieldMaxSize; + FieldMapping(Info.FieldType, DataLen, iFldType, iSubType, LogSize, LocArray); + iUnits2 := 0; + case Info.Fieldtype of + FIELD_TYPE_NUMERIC: + if Info.FieldTypMod > 0 then + begin + iUnits1 := (Info.FieldTypMod - 4) shr 16 and 65535; + iUnits2 := (Info.FieldTypMod - 4) and 65535; + end else + begin + iUnits1 := NUMERIC_PREC; + iUnits2 := NUMERIC_SCALE; + end; + else + iUnits1 := ifthen(iFldType = fldZSTRING, LogSize - 1, LogSize); + end; + iLen := LogSize; + if (iFldType = fldINT32) and (Pos('nextval(', Info.FieldDefault) > 0) then iSubType := fldstAUTOINC; + iOffset := Offset; + efldvVchk := fldvUNKNOWN; + ValChk.bHasDefVal := Info.FieldDefault <> ''; + ValChk.aDefVal := Info.FieldDefault; + ValChk.bRequired := Info.FieldNotNull; + szName := Info.FieldName; + end; +end; + +procedure LoadPSQLLibrary(LibPQPath: string = ''); + + function GetPSQLProc( ProcName : string ) : pointer; + begin + Result := GetProcAddress( SQLLibraryHandle, PChar(ProcName)); + {$IFDEF M_DEBUG} + if not Assigned(Result) then + LogDebugMessage('PROC', Format('No entry address for procedure "%s"', [ProcName])); + {$ENDIF} + + end; + +begin + if LibPQPath = EmptyStr then LibPQPath := PSQL_DLL; + {$IFDEF M_DEBUG} + LogDebugMessage('LIB', 'Trying load ' + LibPQPath); + {$ENDIF} + if ( SQLLibraryHandle <= HINSTANCE_ERROR ) then + begin + // we must think about some property + {$IFDEF ANDROID} + //internal path + LibPQPath := TPath.Combine(TPath.GetDocumentsPath, LibPQPath); + //external path + // LibPQPath := TPath.Combine(TPath.GetSharedDocumentsPath, LibPQPath); + {$ENDIF} + + SQLLibraryHandle := LoadLibrary(PChar(LibPQPath)); + {$IFDEF M_DEBUG} + LogDebugMessage('LIB', 'Handle for module: ' + IntToStr(SQLLibraryHandle)); + {$ENDIF} + if ( SQLLibraryHandle > HINSTANCE_ERROR ) then + begin + @PQlibVersion := GetPSQLProc('PQlibVersion'); + @PQisthreadsafe := GetPSQLProc('PQisthreadsafe'); + @PQconnectdb := GetPSQLProc('PQconnectdb'); + @PQconnectdbParams := GetPSQLProc('PQconnectdbParams'); + @PQping := GetPSQLProc('PQping'); + @PQpingParams := GetPSQLProc('PQpingParams'); + @PQconnectPoll := GetPSQLProc('PQconnectPoll'); + @PQconnectStart := GetPSQLProc('PQconnectStart'); + @PQsetdbLogin := GetPSQLProc('PQsetdbLogin'); + @PQconndefaults := GetPSQLProc('PQconndefaults'); + @PQfinish := GetPSQLProc('PQfinish'); + @PQreset := GetPSQLProc('PQreset'); + @PQrequestCancel := GetPSQLProc('PQrequestCancel'); + @PQdb := GetPSQLProc('PQdb'); + @PQuser := GetPSQLProc('PQuser'); + @PQpass := GetPSQLProc('PQpass'); + @PQhost := GetPSQLProc('PQhost'); + @PQport := GetPSQLProc('PQport'); + @PQtty := GetPSQLProc('PQtty'); + @PQoptions := GetPSQLProc('PQoptions'); + @PQstatus := GetPSQLProc('PQstatus'); + @PQerrorMessage := GetPSQLProc('PQerrorMessage'); + @PQsocket := GetPSQLProc('PQsocket'); + @PQbackendPID := GetPSQLProc('PQbackendPID'); + @PQparameterStatus := GetPSQLProc('PQparameterStatus'); + @PQserverVersion:= GetPSQLProc('PQserverVersion'); + @PQtrace := GetPSQLProc('PQtrace'); + @PQuntrace := GetPSQLProc('PQuntrace'); + @PQsetNoticeProcessor := GetPSQLProc('PQsetNoticeProcessor'); + @PQexecPrepared := GetPSQLProc('PQexecPrepared'); + @PQprepare := GetPSQLProc('PQprepare'); + @PQexec := GetPSQLProc('PQexec'); + @PQsetSingleRowMode := GetPSQLProc('PQsetSingleRowMode'); + @PQexecParams := GetPSQLProc('PQexecParams'); + @PQnotifies := GetPSQLProc('PQnotifies'); + @PQsendQuery := GetPSQLProc('PQsendQuery'); + @PQgetResult := GetPSQLProc('PQgetResult'); + @PQisBusy := GetPSQLProc('PQisBusy'); + @PQconsumeInput := GetPSQLProc('PQconsumeInput'); + @PQgetline := GetPSQLProc('PQgetline'); + @PQputline := GetPSQLProc('PQputline'); + @PQgetlineAsync := GetPSQLProc('PQgetlineAsync'); + @PQputnbytes := GetPSQLProc('PQputnbytes'); + @PQendcopy := GetPSQLProc('PQendcopy'); + @PQgetCopyData := GetPSQLProc('PQgetCopyData'); + @PQputCopyData := GetPSQLProc('PQputCopyData'); + @PQputCopyEnd := GetPSQLProc('PQputCopyEnd'); + @PQresultStatus := GetPSQLProc('PQresultStatus'); + @PQresultErrorMessage := GetPSQLProc('PQresultErrorMessage'); + @PQresultErrorField := GetPSQLProc('PQresultErrorField'); + @PQntuples := GetPSQLProc('PQntuples'); + @PQnfields := GetPSQLProc('PQnfields'); + @PQbinaryTuples := GetPSQLProc('PQbinaryTuples'); + @PQfname := GetPSQLProc('PQfname'); + @PQfnumber := GetPSQLProc('PQfnumber'); + @PQftype := GetPSQLProc('PQftype'); + @PQfformat := GetPSQLProc('PQfformat'); + @PQftable := GetPSQLProc('PQftable'); + @PQftablecol := GetPSQLProc('PQftablecol'); + @PQfsize := GetPSQLProc('PQfsize'); + @PQfmod := GetPSQLProc('PQfmod'); + @PQcmdStatus := GetPSQLProc('PQcmdStatus'); + @PQoidValue := GetPSQLProc('PQoidValue'); + @PQoidStatus := GetPSQLProc('PQoidStatus'); + @PQcmdTuples := GetPSQLProc('PQcmdTuples'); + @PQgetvalue := GetPSQLProc('PQgetvalue'); + @PQsetvalue := GetPSQLProc('PQsetvalue'); + @PQcopyResult := GetPSQLProc('PQcopyResult'); + @PQgetlength := GetPSQLProc('PQgetlength'); + @PQgetisnull := GetPSQLProc('PQgetisnull'); + @PQclear := GetPSQLProc('PQclear'); + @PQmakeEmptyPGresult := GetPSQLProc('PQmakeEmptyPGresult'); + @PQtransactionStatus := GetPSQLProc('PQtransactionStatus'); + @PQEscapeByteaConn := GetPSQLProc('PQescapeByteaConn'); + @PQUnEscapeBytea:= GetPSQLProc('PQunescapeBytea'); + @PQEscapeStringConn:=GetPSQLProc('PQescapeStringConn'); + @PQFreeMem := GetPSQLProc('PQfreemem'); + @PQsetClientEncoding := GetPSQLProc('PQsetClientEncoding'); + @PQsetErrorVerbosity := GetPSQLProc('PQsetErrorVerbosity'); + @PQclientEncoding := GetPSQLProc('PQclientEncoding'); + @PQgetssl := GetPSQLProc('PQgetssl'); + @pg_encoding_to_char := GetPSQLProc('pg_encoding_to_char'); + @lo_open := GetPSQLProc('lo_open'); + @lo_close := GetPSQLProc('lo_close'); + @lo_read := GetPSQLProc('lo_read'); + @lo_write := GetPSQLProc('lo_write'); + @lo_lseek := GetPSQLProc('lo_lseek'); + @lo_creat := GetPSQLProc('lo_creat'); + @lo_tell := GetPSQLProc('lo_tell'); + @lo_unlink := GetPSQLProc('lo_unlink'); + @lo_import := GetPSQLProc('lo_import'); + @lo_export := GetPSQLProc('lo_export'); + end + else + CheckLibraryLoaded(); + {$IFDEF M_DEBUG} + LogDebugMessage('LIB', GetModuleName(GetModuleHandle(PChar(LIBEAY_DLL)))); + LogDebugMessage('LIB', GetModuleName(GetModuleHandle(PChar(SSLEAY_DLL)))); + LogDebugMessage('LIB', GetModuleName(SQLLibraryHandle)); + {$ENDIF} + end; +end; + +procedure UnloadPSQLLibrary; +begin + if IsLibraryLoaded() then + FreeLibrary( SQLLibraryHandle ); + SQLLibraryHandle := HINSTANCE_ERROR; +end; + +procedure HotLibrarySwitch(const ALibPath: string); +var I: integer; +begin + for I := 0 to DBList.Count - 1 do + TPSQLDatabase(DBList[i]).Close; + UnloadPSQLLibrary; + LoadPSQLLibrary(ALibPath); +end; + +procedure CheckLibraryLoaded; +begin + if not IsLibraryLoaded() then + {$IFDEF DELPHI_5} + RaiseLastWin32Error; + {$ELSE} + RaiseLastOSError; + {$ENDIF} +end; + +function IsLibraryLoaded: boolean; +begin + Result := SQLLibraryHandle > HINSTANCE_ERROR; +end; + +function MaskSearch(const Str, Mask: string; + CaseSensitive : boolean = true; + MaskChar: Char = '?'; + WildCard: Char = '%'): Boolean;//mi:2006-09-07 +var + S, M : PChar; + W : PChar; //mi:2007-06-20 last wildcard position in mask +begin + Result := false; + if CaseSensitive then + begin + S := PChar(Str); + M := PChar(Mask); + end + else + begin + S := PChar(AnsiUpperCase(Str)); + M := PChar(AnsiUpperCase(Mask)); + end; + + W := nil; + + while true do + begin + if (S^ = #0) or (M^ = #0) then//we have an end of one of strings + begin + //mi:2007-10-14 there can be some more wildcard chars in mask + while (M^ = WildCard) do + inc(M); + + if S^ = M^ then //both are #0, it seems that we have a match or both strings are empty + Result := true; + exit; + end; + + if (M^ <> MaskChar) and (M^ <> WildCard) then + begin + if M^ = S^ then + begin + Inc(M); Inc(S); //move to the next character + continue; + end + else//character are not equal + begin + if W <> nil then//there was a wildcard before, we need to rollback mask to it to continue search + begin + M := W; + Inc(S); + continue; + end + else //there were no wildcards before, string doesn't match + exit; + end; + end + else if (M^ = MaskChar) then + begin + Inc(M); Inc(S); //move to the next character + continue; + end + else if (M^ = WildCard) then + begin + W := M; + while (S^ <> (M+1)^) and (S^ <> #0) do + Inc(S); + Inc(M); + end; + end; +end; + +function Search(Op1,Op2 : Variant; OEM, CaseSen : Boolean; PartLen: Integer):Boolean; +var + S1,S2 : String; +begin + if CaseSen then //case insensitive + begin + Op1 := AnsiUpperCase(Op1); + Op2 := AnsiUpperCase(Op2); + end; + S1 := Op1; + S2 := Op2; + +{$IFDEF MSWINDOWS} + if OEM then + begin + {$IFDEF DELPHI_12} + OemToCharBuff(PAnsiChar(AnsiString(S1)), PWideChar(S1), Length(S1)); + OemToCharBuff(PAnsiChar(AnsiString(S2)), PWideChar(S2), Length(S2)); + {$ELSE} + {$IFNDEF FPC} + OemToCharBuff(PAnsiChar(S1), PAnsiChar(S1), Length(S1)); + OemToCharBuff(PAnsiChar(S2), PAnsiChar(S2), Length(S2)); + {$ENDIF} + {$ENDIF} + end; +{$ENDIF} + + if CaseSen then //case insensitive + begin + if PartLen = 0 then + Result := AnsiStrIComp(PChar(S1), PChar(S2)) = 0 else // Full len + Result := AnsiStrLIComp(PChar(S1), PChar(S2), PartLen) = 0; //Part len + end else + begin + if PartLen = 0 then + Result := AnsiStrComp(PChar(S1), PChar(S2)) = 0 else // Full len + Result := AnsiStrLComp(PChar(S1), PChar(S2), PartLen) = 0; //Part len + end; +end; + +function GetBDEErrorMessage(ErrorCode : Word):String; +begin + case ErrorCode of + DBIERR_BOF: Result :='At beginning of table.'; //8705 + DBIERR_EOF: Result :='At end of table.'; //8706 + DBIERR_NOCURRREC: Result :='No current record.'; //8709 + DBIERR_RECNOTFOUND: Result :='Could not find record.'; //8710 + DBIERR_ENDOFBLOB: Result :='End of BLOB.'; //8711 + DBIERR_INVALIDPARAM: Result :='Invalid parameter.'; //9986 + DBIERR_INVALIDHNDL: Result :='Invalid handle to the function.'; //9990 + DBIERR_NOSUCHINDEX: Result :='Index does not exist.'; //9997 + DBIERR_INVALIDBLOBOFFSET: Result :='Invalid offset into the BLOB.'; //9998 + DBIERR_INVALIDRECSTRUCT: Result :='Invalid record structure.'; //10003 + DBIERR_NOSUCHTABLE: Result :='Table does not exist.'; //10024 + DBIERR_NOSUCHFILTER: Result :='Filter handle is invalid.'; //10050 + DBIERR_NOTSUFFTABLERIGHTS: Result :='Insufficient table rights for operation. Password required.';//10498 + DBIERR_NOTABLOB: Result :='Field is not a BLOB.'; //10753 + DBIERR_TABLEREADONLY: Result :='Table is read only.'; //10763 + DBIERR_NOASSOCINDEX: Result :='No associated index.'; //10764 + DBIERR_QRYEMPTY: Result :='Query string is empty.'; //11886 + DBIERR_NOTSUPPORTED: Result :='Capability not supported.'; //12289 + DBIERR_UPDATEABORT: Result :='Update aborted.'; //13062 + end; +end; + + +function DACAnsiStrAlloc(Size: Cardinal): PAnsiDACChar; +begin +{$IFNDEF MOBILE} + {$IFDEF DELPHI_12} + Result := AnsiStrAlloc(Size); + {$ELSE} + Result := StrAlloc(Size); + {$ENDIF} +{$ELSE} + Inc(Size, SizeOf(Cardinal)); + GetMem(Result, Size); + Cardinal(Pointer(Result)^) := Size; + Inc(Result, SizeOf(Cardinal)); +{$ENDIF} +end; + +procedure DACAnsiStrDispose(Str: PAnsiDACChar); +begin +{$IFNDEF NEXTGEN} +{$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}strdispose(Str); +{$ELSE} +if Str <> nil then + begin + Dec(Str, SizeOf(Cardinal)); + FreeMem(Str, Cardinal(Pointer(Str)^)); + end; +{$ENDIF} +end; + +function DACStrCopy(Dest: PAnsiDACChar; const Source: PAnsiDACChar; MaxLen: Cardinal = 0): PAnsiDACChar; +{$IFDEF NEXTGEN} +var + Len: Cardinal; +{$ENDIF} +begin + if MaxLen = 0 then + MaxLen := Length(Source); + + Result := Dest; + {$IFNDEF NEXTGEN} + {$IFDEF DELPHI_18}System.AnsiStrings.{$ENDIF}StrLCopy(Dest, Source, MaxLen); + {$ELSE} + Len := Length(Source); + if Len > MaxLen then + Len := MaxLen; + Move(Source^, Dest^, Len); + Dest[Len] := #0; + {$ENDIF} +end; + +procedure DACAllocStr(var Dest: PAnsiDACChar; Len: Integer); +//{$IFDEF NEXTGEN} +//var +// i: integer; +//{$ENDIF} +begin +{$IFNDEF NEXTGEN} + {$IFDEF DELPHI_12} + Dest := AnsiStrAlloc(Len + 1); + {$ELSE} + Dest := StrAlloc(Len + 1); + {$ENDIF} +{$ELSE} +// i := Length(String(Source)); + GetMem(Dest, Len+1); + FillChar(Dest^, Len+1, 0); +{$ENDIF} +end; + + +{$IFDEF MOBILE} +// Working with PAnsiChar for Mobile platform + +function DACAnsiStrBufSize(const Str: PAnsiDACChar): Cardinal; +var + P: PAnsiDACChar; +begin + P := Str; + Dec(P, SizeOf(Cardinal)); + Result := Cardinal(Pointer(P)^) - SizeOf(Cardinal); +end; + +function DACStrBufSize(const Str: PAnsiDACChar): Cardinal; +var + P: PAnsiDACChar; +begin + P := Str; + Dec(P, SizeOf(Cardinal)); + Result := Cardinal(Pointer(P)^) - SizeOf(Cardinal); +end; +{$ENDIF} + +{$IFNDEF MACOS_OR_MOBILE} +function GetTickCount: LongWord; +{$IFDEF DELPHI_12}inline;{$ENDIF} +begin + Result := Windows.GetTickCount; +end; +{$ELSE} +function GetTickCount: LongWord;inline; +begin +{$IFNDEF DELPHI_17} + Result := AbsoluteToNanoseconds(UpTime) div 1000000; +{$ELSE} + Result := TThread.GetTickCount; +{$ENDIF} +end; +{$ENDIF;} + + +initialization + SQLLibraryHandle := HINSTANCE_ERROR; + +finalization + UnloadPSQLLibrary; + +end. diff --git a/PSQLfldlinks.dfm b/Source/PSQLfldlinks.dfm similarity index 94% rename from PSQLfldlinks.dfm rename to Source/PSQLfldlinks.dfm index 7a41e81..00a9b89 100644 --- a/PSQLfldlinks.dfm +++ b/Source/PSQLfldlinks.dfm @@ -1,166 +1,166 @@ -object PSQLLinkFields: TPSQLLinkFields - Left = 440 - Top = 276 - ActiveControl = IndexList - BorderStyle = bsDialog - Caption = 'Field Link Designer' - ClientHeight = 265 - ClientWidth = 352 - Color = clBtnFace - ParentFont = True - OldCreateOrder = True - OnCreate = FormCreate - OnDestroy = FormDestroy - PixelsPerInch = 96 - TextHeight = 13 - object Bevel1: TBevel - Left = 4 - Top = 33 - Width = 343 - Height = 190 - Shape = bsFrame - end - object Bevel2: TBevel - Left = 350 - Top = 285 - Width = 341 - Height = 43 - Shape = bsFrame - end - object Label30: TLabel - Left = 13 - Top = 40 - Width = 57 - Height = 13 - Caption = 'D&etail Fields' - FocusControl = DetailList - IsControl = True - end - object Label31: TLabel - Left = 222 - Top = 38 - Width = 62 - Height = 13 - Caption = '&Master Fields' - FocusControl = MasterList - IsControl = True - end - object IndexLabel: TLabel - Left = 4 - Top = 10 - Width = 83 - Height = 13 - Caption = 'A&vailable Indexes' - FocusControl = IndexList - end - object Label2: TLabel - Left = 12 - Top = 142 - Width = 61 - Height = 13 - Caption = '&Joined Fields' - FocusControl = BindList - end - object DetailList: TListBox - Left = 13 - Top = 55 - Width = 117 - Height = 69 - IntegralHeight = True - ItemHeight = 13 - TabOrder = 1 - OnClick = BindingListClick - IsControl = True - end - object MasterList: TListBox - Left = 222 - Top = 54 - Width = 117 - Height = 69 - IntegralHeight = True - ItemHeight = 13 - TabOrder = 2 - OnClick = BindingListClick - IsControl = True - end - object BindList: TListBox - Left = 12 - Top = 157 - Width = 242 - Height = 56 - IntegralHeight = True - ItemHeight = 13 - MultiSelect = True - TabOrder = 3 - OnClick = BindListClick - IsControl = True - end - object IndexList: TComboBox - Left = 109 - Top = 7 - Width = 192 - Height = 21 - Style = csDropDownList - ItemHeight = 13 - TabOrder = 0 - OnChange = IndexListChange - OnClick = IndexListChange - end - object AddButton: TButton - Left = 138 - Top = 74 - Width = 75 - Height = 25 - Caption = '&Add' - TabOrder = 4 - OnClick = AddButtonClick - end - object DeleteButton: TButton - Left = 263 - Top = 157 - Width = 75 - Height = 25 - Caption = '&Delete' - TabOrder = 5 - OnClick = DeleteButtonClick - end - object ClearButton: TButton - Left = 263 - Top = 188 - Width = 75 - Height = 25 - Caption = '&Clear' - TabOrder = 6 - OnClick = ClearButtonClick - end - object Button1: TButton - Left = 100 - Top = 231 - Width = 75 - Height = 25 - Caption = 'OK' - Default = True - ModalResult = 1 - TabOrder = 7 - OnClick = BitBtn1Click - end - object Button2: TButton - Left = 186 - Top = 231 - Width = 75 - Height = 25 - Cancel = True - Caption = 'Cancel' - ModalResult = 2 - TabOrder = 8 - end - object Help: TButton - Left = 272 - Top = 231 - Width = 75 - Height = 25 - Caption = '&Help' - TabOrder = 9 - OnClick = HelpClick - end -end +object PSQLLinkFields: TPSQLLinkFields + Left = 440 + Top = 276 + ActiveControl = IndexList + BorderStyle = bsDialog + Caption = 'Field Link Designer' + ClientHeight = 265 + ClientWidth = 352 + Color = clBtnFace + ParentFont = True + OldCreateOrder = True + OnCreate = FormCreate + OnDestroy = FormDestroy + PixelsPerInch = 96 + TextHeight = 13 + object Bevel1: TBevel + Left = 4 + Top = 33 + Width = 343 + Height = 190 + Shape = bsFrame + end + object Bevel2: TBevel + Left = 350 + Top = 285 + Width = 341 + Height = 43 + Shape = bsFrame + end + object Label30: TLabel + Left = 13 + Top = 40 + Width = 57 + Height = 13 + Caption = 'D&etail Fields' + FocusControl = DetailList + IsControl = True + end + object Label31: TLabel + Left = 222 + Top = 38 + Width = 62 + Height = 13 + Caption = '&Master Fields' + FocusControl = MasterList + IsControl = True + end + object IndexLabel: TLabel + Left = 4 + Top = 10 + Width = 83 + Height = 13 + Caption = 'A&vailable Indexes' + FocusControl = IndexList + end + object Label2: TLabel + Left = 12 + Top = 142 + Width = 61 + Height = 13 + Caption = '&Joined Fields' + FocusControl = BindList + end + object DetailList: TListBox + Left = 13 + Top = 55 + Width = 117 + Height = 69 + IntegralHeight = True + ItemHeight = 13 + TabOrder = 1 + OnClick = BindingListClick + IsControl = True + end + object MasterList: TListBox + Left = 222 + Top = 54 + Width = 117 + Height = 69 + IntegralHeight = True + ItemHeight = 13 + TabOrder = 2 + OnClick = BindingListClick + IsControl = True + end + object BindList: TListBox + Left = 12 + Top = 157 + Width = 242 + Height = 56 + IntegralHeight = True + ItemHeight = 13 + MultiSelect = True + TabOrder = 3 + OnClick = BindListClick + IsControl = True + end + object IndexList: TComboBox + Left = 109 + Top = 7 + Width = 192 + Height = 21 + Style = csDropDownList + ItemHeight = 13 + TabOrder = 0 + OnChange = IndexListChange + OnClick = IndexListChange + end + object AddButton: TButton + Left = 138 + Top = 74 + Width = 75 + Height = 25 + Caption = '&Add' + TabOrder = 4 + OnClick = AddButtonClick + end + object DeleteButton: TButton + Left = 263 + Top = 157 + Width = 75 + Height = 25 + Caption = '&Delete' + TabOrder = 5 + OnClick = DeleteButtonClick + end + object ClearButton: TButton + Left = 263 + Top = 188 + Width = 75 + Height = 25 + Caption = '&Clear' + TabOrder = 6 + OnClick = ClearButtonClick + end + object Button1: TButton + Left = 100 + Top = 231 + Width = 75 + Height = 25 + Caption = 'OK' + Default = True + ModalResult = 1 + TabOrder = 7 + OnClick = BitBtn1Click + end + object Button2: TButton + Left = 186 + Top = 231 + Width = 75 + Height = 25 + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 8 + end + object Help: TButton + Left = 272 + Top = 231 + Width = 75 + Height = 25 + Caption = '&Help' + TabOrder = 9 + OnClick = HelpClick + end +end diff --git a/PSQLfldlinks.pas b/Source/PSQLfldlinks.pas similarity index 96% rename from PSQLfldlinks.pas rename to Source/PSQLfldlinks.pas index 2ab70ca..37255bf 100644 --- a/PSQLfldlinks.pas +++ b/Source/PSQLfldlinks.pas @@ -1,543 +1,543 @@ -{$I PSQLdac.inc} -unit PSQLfldlinks; - -{SVN revision: $Id$} - -interface - -uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, - StdCtrls, ExtCtrls, DB, Buttons, - {$IFDEF FPC} - PropEdits - {$ELSE} - {$IFNDEF DELPHI_6}DsgnIntf{$ELSE}DesignIntf, DesignEditors{$ENDIF} - {$ENDIF FPC}; - -type - -{ TFieldLink } - - TPSQLFieldLinkProperty = class(TStringProperty) - private - FChanged: Boolean; - FDataSet: TDataSet; - protected - function GetDataSet: TDataSet; - procedure GetFieldNamesForIndex(List: TStrings); virtual; - function GetIndexBased: Boolean; virtual; - function GetIndexDefs: TIndexDefs; virtual; - function GetIndexFieldNames: string; virtual; - function GetIndexName: string; virtual; - function GetMasterFields: string; virtual; abstract; - procedure SetIndexFieldNames(const Value: string); virtual; - procedure SetIndexName(const Value: string); virtual; - procedure SetMasterFields(const Value: string); virtual; abstract; - public - constructor CreateWith(ADataSet: TDataSet); virtual; - procedure GetIndexNames(List: TStrings); - property IndexBased: Boolean read GetIndexBased; - property IndexDefs: TIndexDefs read GetIndexDefs; - property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames; - property IndexName: string read GetIndexName write SetIndexName; - property MasterFields: string read GetMasterFields write SetMasterFields; - property Changed: Boolean read FChanged; - procedure Edit; override; - function GetAttributes: TPropertyAttributes; override; - property DataSet: TDataSet read GetDataSet; - end; - -{ TLink Fields } - - TPSQLLinkFields = class(TForm) - DetailList: TListBox; - MasterList: TListBox; - BindList: TListBox; - Label30: TLabel; - Label31: TLabel; - IndexList: TComboBox; - IndexLabel: TLabel; - Label2: TLabel; - Bevel1: TBevel; - Bevel2: TBevel; - AddButton: TButton; - DeleteButton: TButton; - ClearButton: TButton; - Button1: TButton; - Button2: TButton; - Help: TButton; - procedure FormCreate(Sender: TObject); - procedure BindingListClick(Sender: TObject); - procedure AddButtonClick(Sender: TObject); - procedure DeleteButtonClick(Sender: TObject); - procedure BindListClick(Sender: TObject); - procedure ClearButtonClick(Sender: TObject); - procedure FormDestroy(Sender: TObject); - procedure BitBtn1Click(Sender: TObject); - procedure HelpClick(Sender: TObject); - procedure IndexListChange(Sender: TObject); - private - FDataSet: TDataSet; - FMasterDataSet: TDataSet; - FDataSetProxy: TPSQLFieldLinkProperty; - FFullIndexName: string; - MasterFieldList: string; - IndexFieldList: string; - OrderedDetailList: TStringList; - OrderedMasterList: TStringList; - procedure OrderFieldList(OrderedList, List: TStrings); - procedure AddToBindList(const Str1, Str2: string); - procedure Initialize; - property FullIndexName: string read FFullIndexName; - procedure SetDataSet(Value: TDataSet); - public - property DataSet: TDataSet read FDataSet write SetDataSet; - property DataSetProxy: TPSQLFieldLinkProperty read FDataSetProxy write FDataSetProxy; - function Edit: Boolean; - end; - -function EditMasterFields(ADataSet: TDataSet; ADataSetProxy: TPSQLFieldLinkProperty): Boolean; - -resourcestring - SPrimary = 'Primary'; - SMissingDataSet = 'Missing DataSet property'; - SLinkDesigner = 'Field ''%s'', from the Detail Fields list, must be linked'; - -implementation - -{$R *.DFM} - -uses Dialogs, {$IFNDEF FPC}DBConsts, LibHelp,{$ENDIF} TypInfo; - -{ Utility Functions } - -function StripFieldName(const Fields: string; var Pos: Integer): string; -var - I: Integer; -begin - I := Pos; - while (I <= Length(Fields)) and (Fields[I] <> ';') do Inc(I); - Result := Copy(Fields, Pos, I - Pos); - if (I <= Length(Fields)) and (Fields[I] = ';') then Inc(I); - Pos := I; -end; - -function StripDetail(const Value: string): string; -var - S: string; - I: Integer; -begin - S := Value; - I := 0; - while Pos('->', S) > 0 do - begin - I := Pos('->', S); - S[I] := ' '; - end; - Result := Copy(Value, 0, I - 2); -end; - -function StripMaster(const Value: string): string; -var - S: string; - I: Integer; -begin - S := Value; - I := 0; - while Pos('->', S) > 0 do - begin - I := Pos('->', S); - S[I] := ' '; - end; - Result := Copy(Value, I + 3, Length(Value)); -end; - -function EditMasterFields(ADataSet: TDataSet; ADataSetProxy: TPSQLFieldLinkProperty): Boolean; -begin - with TPSQLLinkFields.Create(nil) do - try - DataSetProxy := ADataSetProxy; - DataSet := ADataSet; - Result := Edit; - finally - Free; - end; -end; - -{ TPSQLFieldLinkProperty } - -function TPSQLFieldLinkProperty.GetIndexBased: Boolean; -begin - Result := False; -end; - -function TPSQLFieldLinkProperty.GetIndexDefs: TIndexDefs; -begin - Result := nil; -end; - -function TPSQLFieldLinkProperty.GetIndexFieldNames: string; -begin - Result := ''; -end; - -function TPSQLFieldLinkProperty.GetIndexName: string; -begin - Result := ''; -end; - -procedure TPSQLFieldLinkProperty.GetIndexNames(List: TStrings); -var - i: Integer; -begin - if IndexDefs <> nil then - begin - for i := 0 to IndexDefs.Count - 1 do - if (ixPrimary in IndexDefs.Items[i].Options) and - (IndexDefs.Items[i].Name = '') then - List.Add(SPrimary) else - List.Add(IndexDefs.Items[i].Name); - end; -end; - -procedure TPSQLFieldLinkProperty.GetFieldNamesForIndex(List: TStrings); -begin -end; - -procedure TPSQLFieldLinkProperty.SetIndexFieldNames(const Value: string); -begin -end; - -procedure TPSQLFieldLinkProperty.SetIndexName(const Value: string); -begin -end; - -function TPSQLFieldLinkProperty.GetAttributes: TPropertyAttributes; -begin - Result := [paDialog]; -end; - -procedure TPSQLFieldLinkProperty.Edit; -begin - FChanged := EditMasterFields(DataSet, Self); - if FChanged then Modified; -end; - -constructor TPSQLFieldLinkProperty.CreateWith(ADataSet: TDataSet); -begin - FDataSet := ADataSet; -end; - -function TPSQLFieldLinkProperty.GetDataSet: TDataSet; -begin - if FDataSet = nil then - FDataSet := TDataSet(GetComponent(0)); - Result := FDataSet; -end; - -{ TLinkFields } - -procedure TPSQLLinkFields.FormCreate(Sender: TObject); -begin - OrderedDetailList := TStringList.Create; - OrderedMasterList := TStringList.Create; - {$IFNDEF FPC} - HelpContext := hcDFieldLinksDesign; - {$ENDIF} -end; - -procedure TPSQLLinkFields.FormDestroy(Sender: TObject); -begin - OrderedDetailList.Free; - OrderedMasterList.Free; -end; - -function TPSQLLinkFields.Edit; -begin - Initialize; - if ShowModal = mrOK then - begin - if FullIndexName <> '' then - DataSetProxy.IndexName := FullIndexName else - DataSetProxy.IndexFieldNames := IndexFieldList; - DataSetProxy.MasterFields := MasterFieldList; - Result := True; - end - else - Result := False; -end; - -procedure TPSQLLinkFields.SetDataSet(Value: TDataSet); -var - IndexDefs: TIndexDefs; -begin - Value.FieldDefs.Update; - IndexDefs := DataSetProxy.IndexDefs; - if Assigned(IndexDefs) then IndexDefs.Update; - if not Assigned(Value.DataSource) or not Assigned(Value.DataSource.DataSet) then - DatabaseError(SMissingDataSet, Value); - Value.DataSource.DataSet.FieldDefs.Update; - FDataSet := Value; - FMasterDataSet := Value.DataSource.DataSet; -end; - -procedure TPSQLLinkFields.Initialize; -var - SIndexName: string; - - procedure SetUpLists(const MasterFieldList, DetailFieldList: string); - var - I, J: Integer; - MasterFieldName, DetailFieldName: string; - begin - I := 1; - J := 1; - while (I <= Length(MasterFieldList)) and (J <= Length(DetailFieldList)) do - begin - MasterFieldName := StripFieldName(MasterFieldList, I); - DetailFieldName := StripFieldName(DetailFieldList, J); - if (MasterList.Items.IndexOf(MasterFieldName) <> -1) and - (OrderedDetailList.IndexOf(DetailFieldName) <> -1) then - begin - with OrderedDetailList do - Objects[IndexOf(DetailFieldName)] := TObject(True); - with DetailList.Items do Delete(IndexOf(DetailFieldName)); - with MasterList.Items do Delete(IndexOf(MasterFieldName)); - BindList.Items.Add(Format('%s -> %s', - [DetailFieldName, MasterFieldName])); - ClearButton.Enabled := True; - end; - end; - end; - -begin - if not DataSetProxy.IndexBased then - begin - IndexLabel.Visible := False; - IndexList.Visible := False; - end - else with DataSetProxy do - begin - GetIndexNames(IndexList.Items); - if IndexFieldNames <> '' then - SIndexName := IndexDefs.FindIndexForFields(IndexFieldNames).Name - else SIndexName := IndexName; - if (SIndexName <> '') and (IndexList.Items.IndexOf(SIndexName) >= 0) then - IndexList.ItemIndex := IndexList.Items.IndexOf(SIndexName) else - IndexList.ItemIndex := 0; - end; - with DataSetProxy do - begin - MasterFieldList := MasterFields; - if (IndexFieldNames = '') and (IndexName <> '') and - (IndexDefs.IndexOf(IndexName) >=0) then - IndexFieldList := IndexDefs[IndexDefs.IndexOf(IndexName)].Fields else - IndexFieldList := IndexFieldNames; - end; - IndexListChange(nil); - FMasterDataSet.GetFieldNames(MasterList.Items); - OrderedMasterList.Assign(MasterList.Items); - SetUpLists(MasterFieldList, IndexFieldList); -end; - -procedure TPSQLLinkFields.IndexListChange(Sender: TObject); -var - I: Integer; - IndexExp: string; -begin - DetailList.Items.Clear; - if DataSetProxy.IndexBased then - begin - DataSetProxy.IndexName := IndexList.Text; - I := DataSetProxy.IndexDefs.IndexOf(DataSetProxy.IndexName); - if (I <> -1) then IndexExp := DataSetProxy.IndexDefs.Items[I].Expression; - if IndexExp <> '' then - DetailList.Items.Add(IndexExp) else - DataSetProxy.GetFieldNamesForIndex(DetailList.Items); - end else - DataSet.GetFieldNames(DetailList.Items); - MasterList.Items.Assign(OrderedMasterList); - OrderedDetailList.Assign(DetailList.Items); - for I := 0 to OrderedDetailList.Count - 1 do - OrderedDetailList.Objects[I] := TObject(False); - BindList.Clear; - AddButton.Enabled := False; - ClearButton.Enabled := False; - DeleteButton.Enabled := False; - MasterList.ItemIndex := -1; -end; - -procedure TPSQLLinkFields.OrderFieldList(OrderedList, List: TStrings); -var - I, J: Integer; - MinIndex, Index, FieldIndex: Integer; -begin - for J := 0 to List.Count - 1 do - begin - MinIndex := $7FFF; - FieldIndex := -1; - for I := J to List.Count - 1 do - begin - Index := OrderedList.IndexOf(List[I]); - if Index < MinIndex then - begin - MinIndex := Index; - FieldIndex := I; - end; - end; - List.Move(FieldIndex, J); - end; -end; - -procedure TPSQLLinkFields.AddToBindList(const Str1, Str2: string); -var - I: Integer; - NewField: string; - NewIndex: Integer; -begin - NewIndex := OrderedDetailList.IndexOf(Str1); - NewField := Format('%s -> %s', [Str1, Str2]); - with BindList.Items do - begin - for I := 0 to Count - 1 do - begin - if OrderedDetailList.IndexOf(StripDetail(Strings[I])) > NewIndex then - begin - Insert(I, NewField); - Exit; - end; - end; - Add(NewField); - end; -end; - -procedure TPSQLLinkFields.BindingListClick(Sender: TObject); -begin - AddButton.Enabled := (DetailList.ItemIndex <> LB_ERR) and - (MasterList.ItemIndex <> LB_ERR); -end; - -procedure TPSQLLinkFields.AddButtonClick(Sender: TObject); -var - DetailIndex: Integer; - MasterIndex: Integer; -begin - DetailIndex := DetailList.ItemIndex; - MasterIndex := MasterList.ItemIndex; - AddToBindList(DetailList.Items[DetailIndex], - MasterList.Items[MasterIndex]); - with OrderedDetailList do - Objects[IndexOf(DetailList.Items[DetailIndex])] := TObject(True); - DetailList.Items.Delete(DetailIndex); - MasterList.Items.Delete(MasterIndex); - ClearButton.Enabled := True; - AddButton.Enabled := False; -end; - -procedure TPSQLLinkFields.ClearButtonClick(Sender: TObject); -var - I: Integer; - BindValue: string; -begin - for I := 0 to BindList.Items.Count - 1 do - begin - BindValue := BindList.Items[I]; - DetailList.Items.Add(StripDetail(BindValue)); - MasterList.Items.Add(StripMaster(BindValue)); - end; - BindList.Clear; - ClearButton.Enabled := False; - DeleteButton.Enabled := False; - OrderFieldList(OrderedDetailList, DetailList.Items); - DetailList.ItemIndex := -1; - MasterList.Items.Assign(OrderedMasterList); - for I := 0 to OrderedDetailList.Count - 1 do - OrderedDetailList.Objects[I] := TObject(False); - AddButton.Enabled := False; -end; - -procedure TPSQLLinkFields.DeleteButtonClick(Sender: TObject); -var - I: Integer; -begin - with BindList do - begin - for I := Items.Count - 1 downto 0 do - begin - if Selected[I] then - begin - DetailList.Items.Add(StripDetail(Items[I])); - MasterList.Items.Add(StripMaster(Items[I])); - with OrderedDetailList do - Objects[IndexOf(StripDetail(Items[I]))] := TObject(False); - Items.Delete(I); - end; - end; - if Items.Count > 0 then Selected[0] := True; - DeleteButton.Enabled := Items.Count > 0; - ClearButton.Enabled := Items.Count > 0; - OrderFieldList(OrderedDetailList, DetailList.Items); - DetailList.ItemIndex := -1; - OrderFieldList(OrderedMasterList, MasterList.Items); - MasterList.ItemIndex := -1; - AddButton.Enabled := False; - end; -end; - -procedure TPSQLLinkFields.BindListClick(Sender: TObject); -begin - DeleteButton.Enabled := BindList.ItemIndex <> LB_ERR; -end; - -procedure TPSQLLinkFields.BitBtn1Click(Sender: TObject); -var - Gap: Boolean; - I: Integer; - FirstIndex: Integer; -begin - FirstIndex := -1; - MasterFieldList := ''; - IndexFieldList := ''; - FFullIndexName := ''; - if DataSetProxy.IndexBased then - begin - Gap := False; - for I := 0 to OrderedDetailList.Count - 1 do - begin - if Boolean(OrderedDetailList.Objects[I]) then - begin - if Gap then - begin - MessageDlg(Format(SLinkDesigner, - [OrderedDetailList[FirstIndex]]), mtError, [mbOK], 0); - ModalResult := 0; - DetailList.ItemIndex := DetailList.Items.IndexOf(OrderedDetailList[FirstIndex]); - Exit; - end; - end - else begin - Gap := True; - if FirstIndex = -1 then FirstIndex := I; - end; - end; - if not Gap then FFullIndexName := DataSetProxy.IndexName; - end; - with BindList do - begin - for I := 0 to Items.Count - 1 do - begin - MasterFieldList := Format('%s%s;', [MasterFieldList, StripMaster(Items[I])]); - IndexFieldList := Format('%s%s;', [IndexFieldList, StripDetail(Items[I])]); - end; - if MasterFieldList <> '' then - SetLength(MasterFieldList, Length(MasterFieldList) - 1); - if IndexFieldList <> '' then - SetLength(IndexFieldList, Length(IndexFieldList) - 1); - end; -end; - -procedure TPSQLLinkFields.HelpClick(Sender: TObject); -begin - Application.HelpContext(HelpContext); -end; - -end. +{$I PSQLdac.inc} +unit PSQLfldlinks; + +{SVN revision: $Id$} + +interface + +uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, + StdCtrls, ExtCtrls, DB, Buttons, + {$IFDEF FPC} + PropEdits + {$ELSE} + {$IFNDEF DELPHI_6}DsgnIntf{$ELSE}DesignIntf, DesignEditors{$ENDIF} + {$ENDIF FPC}; + +type + +{ TFieldLink } + + TPSQLFieldLinkProperty = class(TStringProperty) + private + FChanged: Boolean; + FDataSet: TDataSet; + protected + function GetDataSet: TDataSet; + procedure GetFieldNamesForIndex(List: TStrings); virtual; + function GetIndexBased: Boolean; virtual; + function GetIndexDefs: TIndexDefs; virtual; + function GetIndexFieldNames: string; virtual; + function GetIndexName: string; virtual; + function GetMasterFields: string; virtual; abstract; + procedure SetIndexFieldNames(const Value: string); virtual; + procedure SetIndexName(const Value: string); virtual; + procedure SetMasterFields(const Value: string); virtual; abstract; + public + constructor CreateWith(ADataSet: TDataSet); virtual; + procedure GetIndexNames(List: TStrings); + property IndexBased: Boolean read GetIndexBased; + property IndexDefs: TIndexDefs read GetIndexDefs; + property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames; + property IndexName: string read GetIndexName write SetIndexName; + property MasterFields: string read GetMasterFields write SetMasterFields; + property Changed: Boolean read FChanged; + procedure Edit; override; + function GetAttributes: TPropertyAttributes; override; + property DataSet: TDataSet read GetDataSet; + end; + +{ TLink Fields } + + TPSQLLinkFields = class(TForm) + DetailList: TListBox; + MasterList: TListBox; + BindList: TListBox; + Label30: TLabel; + Label31: TLabel; + IndexList: TComboBox; + IndexLabel: TLabel; + Label2: TLabel; + Bevel1: TBevel; + Bevel2: TBevel; + AddButton: TButton; + DeleteButton: TButton; + ClearButton: TButton; + Button1: TButton; + Button2: TButton; + Help: TButton; + procedure FormCreate(Sender: TObject); + procedure BindingListClick(Sender: TObject); + procedure AddButtonClick(Sender: TObject); + procedure DeleteButtonClick(Sender: TObject); + procedure BindListClick(Sender: TObject); + procedure ClearButtonClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure BitBtn1Click(Sender: TObject); + procedure HelpClick(Sender: TObject); + procedure IndexListChange(Sender: TObject); + private + FDataSet: TDataSet; + FMasterDataSet: TDataSet; + FDataSetProxy: TPSQLFieldLinkProperty; + FFullIndexName: string; + MasterFieldList: string; + IndexFieldList: string; + OrderedDetailList: TStringList; + OrderedMasterList: TStringList; + procedure OrderFieldList(OrderedList, List: TStrings); + procedure AddToBindList(const Str1, Str2: string); + procedure Initialize; + property FullIndexName: string read FFullIndexName; + procedure SetDataSet(Value: TDataSet); + public + property DataSet: TDataSet read FDataSet write SetDataSet; + property DataSetProxy: TPSQLFieldLinkProperty read FDataSetProxy write FDataSetProxy; + function Edit: Boolean; + end; + +function EditMasterFields(ADataSet: TDataSet; ADataSetProxy: TPSQLFieldLinkProperty): Boolean; + +resourcestring + SPrimary = 'Primary'; + SMissingDataSet = 'Missing DataSet property'; + SLinkDesigner = 'Field ''%s'', from the Detail Fields list, must be linked'; + +implementation + +{$R *.DFM} + +uses Dialogs, {$IFNDEF FPC}DBConsts, LibHelp,{$ENDIF} TypInfo; + +{ Utility Functions } + +function StripFieldName(const Fields: string; var Pos: Integer): string; +var + I: Integer; +begin + I := Pos; + while (I <= Length(Fields)) and (Fields[I] <> ';') do Inc(I); + Result := Copy(Fields, Pos, I - Pos); + if (I <= Length(Fields)) and (Fields[I] = ';') then Inc(I); + Pos := I; +end; + +function StripDetail(const Value: string): string; +var + S: string; + I: Integer; +begin + S := Value; + I := 0; + while Pos('->', S) > 0 do + begin + I := Pos('->', S); + S[I] := ' '; + end; + Result := Copy(Value, 0, I - 2); +end; + +function StripMaster(const Value: string): string; +var + S: string; + I: Integer; +begin + S := Value; + I := 0; + while Pos('->', S) > 0 do + begin + I := Pos('->', S); + S[I] := ' '; + end; + Result := Copy(Value, I + 3, Length(Value)); +end; + +function EditMasterFields(ADataSet: TDataSet; ADataSetProxy: TPSQLFieldLinkProperty): Boolean; +begin + with TPSQLLinkFields.Create(nil) do + try + DataSetProxy := ADataSetProxy; + DataSet := ADataSet; + Result := Edit; + finally + Free; + end; +end; + +{ TPSQLFieldLinkProperty } + +function TPSQLFieldLinkProperty.GetIndexBased: Boolean; +begin + Result := False; +end; + +function TPSQLFieldLinkProperty.GetIndexDefs: TIndexDefs; +begin + Result := nil; +end; + +function TPSQLFieldLinkProperty.GetIndexFieldNames: string; +begin + Result := ''; +end; + +function TPSQLFieldLinkProperty.GetIndexName: string; +begin + Result := ''; +end; + +procedure TPSQLFieldLinkProperty.GetIndexNames(List: TStrings); +var + i: Integer; +begin + if IndexDefs <> nil then + begin + for i := 0 to IndexDefs.Count - 1 do + if (ixPrimary in IndexDefs.Items[i].Options) and + (IndexDefs.Items[i].Name = '') then + List.Add(SPrimary) else + List.Add(IndexDefs.Items[i].Name); + end; +end; + +procedure TPSQLFieldLinkProperty.GetFieldNamesForIndex(List: TStrings); +begin +end; + +procedure TPSQLFieldLinkProperty.SetIndexFieldNames(const Value: string); +begin +end; + +procedure TPSQLFieldLinkProperty.SetIndexName(const Value: string); +begin +end; + +function TPSQLFieldLinkProperty.GetAttributes: TPropertyAttributes; +begin + Result := [paDialog]; +end; + +procedure TPSQLFieldLinkProperty.Edit; +begin + FChanged := EditMasterFields(DataSet, Self); + if FChanged then Modified; +end; + +constructor TPSQLFieldLinkProperty.CreateWith(ADataSet: TDataSet); +begin + FDataSet := ADataSet; +end; + +function TPSQLFieldLinkProperty.GetDataSet: TDataSet; +begin + if FDataSet = nil then + FDataSet := TDataSet(GetComponent(0)); + Result := FDataSet; +end; + +{ TLinkFields } + +procedure TPSQLLinkFields.FormCreate(Sender: TObject); +begin + OrderedDetailList := TStringList.Create; + OrderedMasterList := TStringList.Create; + {$IFNDEF FPC} + HelpContext := hcDFieldLinksDesign; + {$ENDIF} +end; + +procedure TPSQLLinkFields.FormDestroy(Sender: TObject); +begin + OrderedDetailList.Free; + OrderedMasterList.Free; +end; + +function TPSQLLinkFields.Edit; +begin + Initialize; + if ShowModal = mrOK then + begin + if FullIndexName <> '' then + DataSetProxy.IndexName := FullIndexName else + DataSetProxy.IndexFieldNames := IndexFieldList; + DataSetProxy.MasterFields := MasterFieldList; + Result := True; + end + else + Result := False; +end; + +procedure TPSQLLinkFields.SetDataSet(Value: TDataSet); +var + IndexDefs: TIndexDefs; +begin + Value.FieldDefs.Update; + IndexDefs := DataSetProxy.IndexDefs; + if Assigned(IndexDefs) then IndexDefs.Update; + if not Assigned(Value.DataSource) or not Assigned(Value.DataSource.DataSet) then + DatabaseError(SMissingDataSet, Value); + Value.DataSource.DataSet.FieldDefs.Update; + FDataSet := Value; + FMasterDataSet := Value.DataSource.DataSet; +end; + +procedure TPSQLLinkFields.Initialize; +var + SIndexName: string; + + procedure SetUpLists(const MasterFieldList, DetailFieldList: string); + var + I, J: Integer; + MasterFieldName, DetailFieldName: string; + begin + I := 1; + J := 1; + while (I <= Length(MasterFieldList)) and (J <= Length(DetailFieldList)) do + begin + MasterFieldName := StripFieldName(MasterFieldList, I); + DetailFieldName := StripFieldName(DetailFieldList, J); + if (MasterList.Items.IndexOf(MasterFieldName) <> -1) and + (OrderedDetailList.IndexOf(DetailFieldName) <> -1) then + begin + with OrderedDetailList do + Objects[IndexOf(DetailFieldName)] := TObject(True); + with DetailList.Items do Delete(IndexOf(DetailFieldName)); + with MasterList.Items do Delete(IndexOf(MasterFieldName)); + BindList.Items.Add(Format('%s -> %s', + [DetailFieldName, MasterFieldName])); + ClearButton.Enabled := True; + end; + end; + end; + +begin + if not DataSetProxy.IndexBased then + begin + IndexLabel.Visible := False; + IndexList.Visible := False; + end + else with DataSetProxy do + begin + GetIndexNames(IndexList.Items); + if IndexFieldNames <> '' then + SIndexName := IndexDefs.FindIndexForFields(IndexFieldNames).Name + else SIndexName := IndexName; + if (SIndexName <> '') and (IndexList.Items.IndexOf(SIndexName) >= 0) then + IndexList.ItemIndex := IndexList.Items.IndexOf(SIndexName) else + IndexList.ItemIndex := 0; + end; + with DataSetProxy do + begin + MasterFieldList := MasterFields; + if (IndexFieldNames = '') and (IndexName <> '') and + (IndexDefs.IndexOf(IndexName) >=0) then + IndexFieldList := IndexDefs[IndexDefs.IndexOf(IndexName)].Fields else + IndexFieldList := IndexFieldNames; + end; + IndexListChange(nil); + FMasterDataSet.GetFieldNames(MasterList.Items); + OrderedMasterList.Assign(MasterList.Items); + SetUpLists(MasterFieldList, IndexFieldList); +end; + +procedure TPSQLLinkFields.IndexListChange(Sender: TObject); +var + I: Integer; + IndexExp: string; +begin + DetailList.Items.Clear; + if DataSetProxy.IndexBased then + begin + DataSetProxy.IndexName := IndexList.Text; + I := DataSetProxy.IndexDefs.IndexOf(DataSetProxy.IndexName); + if (I <> -1) then IndexExp := DataSetProxy.IndexDefs.Items[I].Expression; + if IndexExp <> '' then + DetailList.Items.Add(IndexExp) else + DataSetProxy.GetFieldNamesForIndex(DetailList.Items); + end else + DataSet.GetFieldNames(DetailList.Items); + MasterList.Items.Assign(OrderedMasterList); + OrderedDetailList.Assign(DetailList.Items); + for I := 0 to OrderedDetailList.Count - 1 do + OrderedDetailList.Objects[I] := TObject(False); + BindList.Clear; + AddButton.Enabled := False; + ClearButton.Enabled := False; + DeleteButton.Enabled := False; + MasterList.ItemIndex := -1; +end; + +procedure TPSQLLinkFields.OrderFieldList(OrderedList, List: TStrings); +var + I, J: Integer; + MinIndex, Index, FieldIndex: Integer; +begin + for J := 0 to List.Count - 1 do + begin + MinIndex := $7FFF; + FieldIndex := -1; + for I := J to List.Count - 1 do + begin + Index := OrderedList.IndexOf(List[I]); + if Index < MinIndex then + begin + MinIndex := Index; + FieldIndex := I; + end; + end; + List.Move(FieldIndex, J); + end; +end; + +procedure TPSQLLinkFields.AddToBindList(const Str1, Str2: string); +var + I: Integer; + NewField: string; + NewIndex: Integer; +begin + NewIndex := OrderedDetailList.IndexOf(Str1); + NewField := Format('%s -> %s', [Str1, Str2]); + with BindList.Items do + begin + for I := 0 to Count - 1 do + begin + if OrderedDetailList.IndexOf(StripDetail(Strings[I])) > NewIndex then + begin + Insert(I, NewField); + Exit; + end; + end; + Add(NewField); + end; +end; + +procedure TPSQLLinkFields.BindingListClick(Sender: TObject); +begin + AddButton.Enabled := (DetailList.ItemIndex <> LB_ERR) and + (MasterList.ItemIndex <> LB_ERR); +end; + +procedure TPSQLLinkFields.AddButtonClick(Sender: TObject); +var + DetailIndex: Integer; + MasterIndex: Integer; +begin + DetailIndex := DetailList.ItemIndex; + MasterIndex := MasterList.ItemIndex; + AddToBindList(DetailList.Items[DetailIndex], + MasterList.Items[MasterIndex]); + with OrderedDetailList do + Objects[IndexOf(DetailList.Items[DetailIndex])] := TObject(True); + DetailList.Items.Delete(DetailIndex); + MasterList.Items.Delete(MasterIndex); + ClearButton.Enabled := True; + AddButton.Enabled := False; +end; + +procedure TPSQLLinkFields.ClearButtonClick(Sender: TObject); +var + I: Integer; + BindValue: string; +begin + for I := 0 to BindList.Items.Count - 1 do + begin + BindValue := BindList.Items[I]; + DetailList.Items.Add(StripDetail(BindValue)); + MasterList.Items.Add(StripMaster(BindValue)); + end; + BindList.Clear; + ClearButton.Enabled := False; + DeleteButton.Enabled := False; + OrderFieldList(OrderedDetailList, DetailList.Items); + DetailList.ItemIndex := -1; + MasterList.Items.Assign(OrderedMasterList); + for I := 0 to OrderedDetailList.Count - 1 do + OrderedDetailList.Objects[I] := TObject(False); + AddButton.Enabled := False; +end; + +procedure TPSQLLinkFields.DeleteButtonClick(Sender: TObject); +var + I: Integer; +begin + with BindList do + begin + for I := Items.Count - 1 downto 0 do + begin + if Selected[I] then + begin + DetailList.Items.Add(StripDetail(Items[I])); + MasterList.Items.Add(StripMaster(Items[I])); + with OrderedDetailList do + Objects[IndexOf(StripDetail(Items[I]))] := TObject(False); + Items.Delete(I); + end; + end; + if Items.Count > 0 then Selected[0] := True; + DeleteButton.Enabled := Items.Count > 0; + ClearButton.Enabled := Items.Count > 0; + OrderFieldList(OrderedDetailList, DetailList.Items); + DetailList.ItemIndex := -1; + OrderFieldList(OrderedMasterList, MasterList.Items); + MasterList.ItemIndex := -1; + AddButton.Enabled := False; + end; +end; + +procedure TPSQLLinkFields.BindListClick(Sender: TObject); +begin + DeleteButton.Enabled := BindList.ItemIndex <> LB_ERR; +end; + +procedure TPSQLLinkFields.BitBtn1Click(Sender: TObject); +var + Gap: Boolean; + I: Integer; + FirstIndex: Integer; +begin + FirstIndex := -1; + MasterFieldList := ''; + IndexFieldList := ''; + FFullIndexName := ''; + if DataSetProxy.IndexBased then + begin + Gap := False; + for I := 0 to OrderedDetailList.Count - 1 do + begin + if Boolean(OrderedDetailList.Objects[I]) then + begin + if Gap then + begin + MessageDlg(Format(SLinkDesigner, + [OrderedDetailList[FirstIndex]]), mtError, [mbOK], 0); + ModalResult := 0; + DetailList.ItemIndex := DetailList.Items.IndexOf(OrderedDetailList[FirstIndex]); + Exit; + end; + end + else begin + Gap := True; + if FirstIndex = -1 then FirstIndex := I; + end; + end; + if not Gap then FFullIndexName := DataSetProxy.IndexName; + end; + with BindList do + begin + for I := 0 to Items.Count - 1 do + begin + MasterFieldList := Format('%s%s;', [MasterFieldList, StripMaster(Items[I])]); + IndexFieldList := Format('%s%s;', [IndexFieldList, StripDetail(Items[I])]); + end; + if MasterFieldList <> '' then + SetLength(MasterFieldList, Length(MasterFieldList) - 1); + if IndexFieldList <> '' then + SetLength(IndexFieldList, Length(IndexFieldList) - 1); + end; +end; + +procedure TPSQLLinkFields.HelpClick(Sender: TObject); +begin + Application.HelpContext(HelpContext); +end; + +end. diff --git a/PSQLupdsqled.dfm b/Source/PSQLupdsqled.dfm similarity index 95% rename from PSQLupdsqled.dfm rename to Source/PSQLupdsqled.dfm index 172a050..7997f1d 100644 --- a/PSQLupdsqled.dfm +++ b/Source/PSQLupdsqled.dfm @@ -1,221 +1,221 @@ -object PSQLUpdateSQLEditForm: TPSQLUpdateSQLEditForm - Left = 397 - Top = 306 - ActiveControl = UpdateTableName - BorderStyle = bsDialog - ClientHeight = 298 - ClientWidth = 473 - Color = clBtnFace - ParentFont = True - OldCreateOrder = True - Position = poScreenCenter - OnCloseQuery = FormCloseQuery - OnCreate = FormCreate - OnDestroy = FormDestroy - PixelsPerInch = 96 - TextHeight = 13 - object OkButton: TButton - Left = 215 - Top = 268 - Width = 75 - Height = 25 - Caption = '&OK' - Default = True - ModalResult = 1 - TabOrder = 0 - OnClick = OkButtonClick - end - object CancelButton: TButton - Left = 303 - Top = 268 - Width = 75 - Height = 25 - Cancel = True - Caption = 'Cancel' - ModalResult = 2 - TabOrder = 1 - end - object HelpButton: TButton - Left = 391 - Top = 268 - Width = 75 - Height = 25 - Caption = '&Help' - TabOrder = 2 - OnClick = HelpButtonClick - end - object PageControl: TPageControl - Left = 6 - Top = 6 - Width = 459 - Height = 253 - ActivePage = FieldsPage - TabOrder = 3 - OnChanging = PageControlChanging - object FieldsPage: TTabSheet - Caption = 'Options' - object GroupBox1: TGroupBox - Left = 8 - Top = 4 - Width = 435 - Height = 212 - Caption = ' SQL Generation ' - TabOrder = 0 - object Label1: TLabel - Left = 12 - Top = 18 - Width = 61 - Height = 13 - Caption = 'Table &Name:' - FocusControl = UpdateTableName - end - object Label3: TLabel - Left = 146 - Top = 18 - Width = 51 - Height = 13 - Caption = '&Key Fields:' - FocusControl = KeyFieldList - end - object Label4: TLabel - Left = 289 - Top = 18 - Width = 68 - Height = 13 - Caption = 'Update &Fields:' - FocusControl = UpdateFieldList - end - object UpdateTableName: TComboBox - Left = 9 - Top = 35 - Width = 130 - Height = 21 - ItemHeight = 13 - TabOrder = 0 - OnChange = UpdateTableNameChange - OnClick = UpdateTableNameClick - end - object KeyFieldList: TListBox - Left = 146 - Top = 35 - Width = 136 - Height = 163 - ItemHeight = 13 - MultiSelect = True - PopupMenu = FieldListPopup - TabOrder = 6 - OnClick = SettingsChanged - end - object UpdateFieldList: TListBox - Left = 289 - Top = 35 - Width = 136 - Height = 163 - ItemHeight = 13 - MultiSelect = True - PopupMenu = FieldListPopup - TabOrder = 7 - OnClick = SettingsChanged - end - object GenerateButton: TButton - Left = 17 - Top = 154 - Width = 114 - Height = 25 - Caption = '&Generate SQL' - TabOrder = 4 - OnClick = GenerateButtonClick - end - object PrimaryKeyButton: TButton - Left = 17 - Top = 124 - Width = 114 - Height = 25 - Caption = 'Select &Primary Keys' - TabOrder = 3 - OnClick = PrimaryKeyButtonClick - end - object DefaultButton: TButton - Left = 17 - Top = 93 - Width = 114 - Height = 25 - Caption = '&Dataset Defaults' - Enabled = False - TabOrder = 2 - OnClick = DefaultButtonClick - end - object QuoteFields: TCheckBox - Left = 18 - Top = 184 - Width = 119 - Height = 17 - Caption = '&Quote Field Names' - TabOrder = 5 - OnClick = SettingsChanged - end - object GetTableFieldsButton: TButton - Left = 17 - Top = 62 - Width = 114 - Height = 25 - Caption = 'Get &Table Fields' - TabOrder = 1 - OnClick = GetTableFieldsButtonClick - end - end - end - object SQLPage: TTabSheet - Caption = 'SQL' - object Label2: TLabel - Left = 13 - Top = 53 - Width = 48 - Height = 13 - Caption = 'S&QL Text:' - FocusControl = SQLMemo - end - object SQLMemo: TMemo - Left = 9 - Top = 69 - Width = 433 - Height = 141 - ScrollBars = ssVertical - TabOrder = 0 - OnKeyPress = SQLMemoKeyPress - end - object StatementType: TRadioGroup - Left = 10 - Top = 6 - Width = 432 - Height = 40 - Caption = 'Statement Type' - Columns = 3 - ItemIndex = 0 - Items.Strings = ( - '&Modify' - '&Insert' - '&Delete') - TabOrder = 1 - OnClick = StatementTypeClick - end - end - end - object FieldListPopup: TPopupMenu - Left = 54 - Top = 262 - object miSelectAll: TMenuItem - Caption = '&Select All' - OnClick = SelectAllClick - end - object miClearAll: TMenuItem - Caption = '&Clear All' - OnClick = ClearAllClick - end - end - object FTempTable: TPSQLTable - Options = [dsoUseGUIDField] - Left = 88 - Top = 264 - end -end +object PSQLUpdateSQLEditForm: TPSQLUpdateSQLEditForm + Left = 397 + Top = 306 + ActiveControl = UpdateTableName + BorderStyle = bsDialog + ClientHeight = 298 + ClientWidth = 473 + Color = clBtnFace + ParentFont = True + OldCreateOrder = True + Position = poScreenCenter + OnCloseQuery = FormCloseQuery + OnCreate = FormCreate + OnDestroy = FormDestroy + PixelsPerInch = 96 + TextHeight = 13 + object OkButton: TButton + Left = 215 + Top = 268 + Width = 75 + Height = 25 + Caption = '&OK' + Default = True + ModalResult = 1 + TabOrder = 0 + OnClick = OkButtonClick + end + object CancelButton: TButton + Left = 303 + Top = 268 + Width = 75 + Height = 25 + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 1 + end + object HelpButton: TButton + Left = 391 + Top = 268 + Width = 75 + Height = 25 + Caption = '&Help' + TabOrder = 2 + OnClick = HelpButtonClick + end + object PageControl: TPageControl + Left = 6 + Top = 6 + Width = 459 + Height = 253 + ActivePage = FieldsPage + TabOrder = 3 + OnChanging = PageControlChanging + object FieldsPage: TTabSheet + Caption = 'Options' + object GroupBox1: TGroupBox + Left = 8 + Top = 4 + Width = 435 + Height = 212 + Caption = ' SQL Generation ' + TabOrder = 0 + object Label1: TLabel + Left = 12 + Top = 18 + Width = 61 + Height = 13 + Caption = 'Table &Name:' + FocusControl = UpdateTableName + end + object Label3: TLabel + Left = 146 + Top = 18 + Width = 51 + Height = 13 + Caption = '&Key Fields:' + FocusControl = KeyFieldList + end + object Label4: TLabel + Left = 289 + Top = 18 + Width = 68 + Height = 13 + Caption = 'Update &Fields:' + FocusControl = UpdateFieldList + end + object UpdateTableName: TComboBox + Left = 9 + Top = 35 + Width = 130 + Height = 21 + ItemHeight = 13 + TabOrder = 0 + OnChange = UpdateTableNameChange + OnClick = UpdateTableNameClick + end + object KeyFieldList: TListBox + Left = 146 + Top = 35 + Width = 136 + Height = 163 + ItemHeight = 13 + MultiSelect = True + PopupMenu = FieldListPopup + TabOrder = 6 + OnClick = SettingsChanged + end + object UpdateFieldList: TListBox + Left = 289 + Top = 35 + Width = 136 + Height = 163 + ItemHeight = 13 + MultiSelect = True + PopupMenu = FieldListPopup + TabOrder = 7 + OnClick = SettingsChanged + end + object GenerateButton: TButton + Left = 17 + Top = 154 + Width = 114 + Height = 25 + Caption = '&Generate SQL' + TabOrder = 4 + OnClick = GenerateButtonClick + end + object PrimaryKeyButton: TButton + Left = 17 + Top = 124 + Width = 114 + Height = 25 + Caption = 'Select &Primary Keys' + TabOrder = 3 + OnClick = PrimaryKeyButtonClick + end + object DefaultButton: TButton + Left = 17 + Top = 93 + Width = 114 + Height = 25 + Caption = '&Dataset Defaults' + Enabled = False + TabOrder = 2 + OnClick = DefaultButtonClick + end + object QuoteFields: TCheckBox + Left = 18 + Top = 184 + Width = 119 + Height = 17 + Caption = '&Quote Field Names' + TabOrder = 5 + OnClick = SettingsChanged + end + object GetTableFieldsButton: TButton + Left = 17 + Top = 62 + Width = 114 + Height = 25 + Caption = 'Get &Table Fields' + TabOrder = 1 + OnClick = GetTableFieldsButtonClick + end + end + end + object SQLPage: TTabSheet + Caption = 'SQL' + object Label2: TLabel + Left = 13 + Top = 53 + Width = 48 + Height = 13 + Caption = 'S&QL Text:' + FocusControl = SQLMemo + end + object SQLMemo: TMemo + Left = 9 + Top = 69 + Width = 433 + Height = 141 + ScrollBars = ssVertical + TabOrder = 0 + OnKeyPress = SQLMemoKeyPress + end + object StatementType: TRadioGroup + Left = 10 + Top = 6 + Width = 432 + Height = 40 + Caption = 'Statement Type' + Columns = 3 + ItemIndex = 0 + Items.Strings = ( + '&Modify' + '&Insert' + '&Delete') + TabOrder = 1 + OnClick = StatementTypeClick + end + end + end + object FieldListPopup: TPopupMenu + Left = 54 + Top = 262 + object miSelectAll: TMenuItem + Caption = '&Select All' + OnClick = SelectAllClick + end + object miClearAll: TMenuItem + Caption = '&Clear All' + OnClick = ClearAllClick + end + end + object FTempTable: TPSQLTable + Options = [dsoUseGUIDField] + Left = 88 + Top = 264 + end +end diff --git a/PSQLupdsqled.pas b/Source/PSQLupdsqled.pas similarity index 96% rename from PSQLupdsqled.pas rename to Source/PSQLupdsqled.pas index 2bb038f..f31e66a 100644 --- a/PSQLupdsqled.pas +++ b/Source/PSQLupdsqled.pas @@ -1,951 +1,951 @@ -{$I psqldac.inc} -unit PSQLupdsqled; - -{SVN revision: $Id$} - -interface - -uses Forms, DB, ExtCtrls, StdCtrls, Controls, ComCtrls, - Classes, SysUtils, Windows, Menus, PSQLDBTables; - -type - - TWaitMethod = procedure of object; - - TPSQLUpdateSQLEditForm = class(TForm) - OkButton: TButton; - CancelButton: TButton; - HelpButton: TButton; - GenerateButton: TButton; - PrimaryKeyButton: TButton; - DefaultButton: TButton; - UpdateTableName: TComboBox; - FieldsPage: TTabSheet; - SQLPage: TTabSheet; - PageControl: TPageControl; - KeyFieldList: TListBox; - UpdateFieldList: TListBox; - GroupBox1: TGroupBox; - Label1: TLabel; - SQLMemo: TMemo; - StatementType: TRadioGroup; - QuoteFields: TCheckBox; - GetTableFieldsButton: TButton; - FieldListPopup: TPopupMenu; - miSelectAll: TMenuItem; - miClearAll: TMenuItem; - procedure FormCreate(Sender: TObject); - procedure HelpButtonClick(Sender: TObject); - procedure StatementTypeClick(Sender: TObject); - procedure OkButtonClick(Sender: TObject); - procedure DefaultButtonClick(Sender: TObject); - procedure GenerateButtonClick(Sender: TObject); - procedure PrimaryKeyButtonClick(Sender: TObject); - procedure PageControlChanging(Sender: TObject; - var AllowChange: Boolean); - procedure FormDestroy(Sender: TObject); - procedure GetTableFieldsButtonClick(Sender: TObject); - procedure SettingsChanged(Sender: TObject); - procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); - procedure UpdateTableNameChange(Sender: TObject); - procedure UpdateTableNameClick(Sender: TObject); - procedure SelectAllClick(Sender: TObject); - procedure ClearAllClick(Sender: TObject); - procedure SQLMemoKeyPress(Sender: TObject; var Key: Char); - private - StmtIndex: Integer; - DataSet: TPSQLDataSet; - Database: TPSQLDatabase; - FTempTable : TPSQLTable; - DatabaseOpened: Boolean; - UpdateSQL: TPSQLUpdateSQL; - FSettingsChanged: Boolean; - FDatasetDefaults: Boolean; - SQLText: array[TUpdateKind] of TStrings; - function GetTableRef(const TabName, QuoteChar: string): string; - function DatabaseOpen: Boolean; - function Edit: Boolean; - procedure GenWhereClause(const TabAlias, QuoteChar: string; - KeyFields, SQL: TStrings); - procedure GenDeleteSQL(const TableName, QuoteChar: string; - KeyFields, SQL: TStrings); - procedure GenInsertSQL(const TableName, QuoteChar: string; - UpdateFields, SQL: TStrings); - procedure GenModifySQL(const TableName, QuoteChar: string; - KeyFields, UpdateFields, SQL: TStrings); - procedure GenerateSQL; - procedure GetDataSetFieldNames; - procedure GetTableFieldNames; - procedure InitGenerateOptions; - procedure InitUpdateTableNames; - procedure SetButtonStates; - procedure SelectPrimaryKeyFields; - procedure SetDefaultSelections; - procedure ShowWait(WaitMethod: TWaitMethod); - function TempTable: TPSQLTable; - end; - -{ TSQLParser } - - TSQLToken = (stSymbol, stAlias, stNumber, stComma, stEQ, stOther, stLParen, - stRParen, stEnd); - - TSQLParser = class - private - FText: string; - FSourcePtr: PChar; - FTokenPtr: PChar; - FTokenString: string; - FToken: TSQLToken; - FSymbolQuoted: Boolean; - function NextToken: TSQLToken; - function TokenSymbolIs(const S: string): Boolean; - procedure Reset; - public - constructor Create(const Text: string); - procedure GetSelectTableNames(List: TStrings); - procedure GetUpdateTableName(var TableName: string); - procedure GetUpdateFields(List: TStrings); - procedure GetWhereFields(List: TStrings); - end; - -function EditPSQLUpdateSQL(AUpdateSQL: TPSQLUpdateSQL): Boolean; - -implementation - -{$R *.DFM} - -uses Dialogs, {$IFNDEF FPC}LibHelp,{$ENDIF} TypInfo, PSQLTypes; - -{ Global Interface functions } - -function EditPSQLUpdateSQL(AUpdateSQL: TPSQLUpdateSQL): Boolean; -begin - with TPSQLUpdateSQLEditForm.Create(Application) do - try - UpdateSQL := AUpdateSQL; - Result := Edit; - finally - Free; - end; -end; - -{ Utility Routines } - -procedure GetSelectedItems(ListBox: TListBox; List: TStrings); -var - I: Integer; -begin - List.Clear; - for I := 0 to ListBox.Items.Count - 1 do - if ListBox.Selected[I] then - List.Add(ListBox.Items[I]); -end; - -function SetSelectedItems(ListBox: TListBox; List: TStrings): Integer; -var - I: Integer; -begin - Result := 0; - ListBox.Items.BeginUpdate; - try - for I := 0 to ListBox.Items.Count - 1 do - if List.IndexOf(ListBox.Items[I]) > -1 then - begin - ListBox.Selected[I] := True; - Inc(Result); - end - else - ListBox.Selected[I] := False; - if ListBox.Items.Count > 0 then - begin - ListBox.ItemIndex := 0; - ListBox.TopIndex := 0; - end; - finally - ListBox.Items.EndUpdate; - end; -end; - -procedure SelectAll(ListBox: TListBox); -var - I: Integer; -begin - ListBox.Items.BeginUpdate; - try - with ListBox do - for I := 0 to Items.Count - 1 do - Selected[I] := True; - if ListBox.Items.Count > 0 then - begin - ListBox.ItemIndex := 0; - ListBox.TopIndex := 0; - end; - finally - ListBox.Items.EndUpdate; - end; -end; - -procedure GetDataFieldNames(Dataset: TDataset; ErrorName: string; List: TStrings); -var - I: Integer; -begin - with Dataset do - try - FieldDefs.Update; - List.BeginUpdate; - try - List.Clear; - for I := 0 to FieldDefs.Count - 1 do - List.Add(FieldDefs[I].Name); - finally - List.EndUpdate; - end; - except - if ErrorName <> '' then - MessageDlg(Format(SSQLDataSetOpen, [ErrorName]), mtError, [mbOK], 0); - end; -end; - -procedure GetSQLTableNames(const SQL: string; List: TStrings); -begin - with TSQLParser.Create(SQL) do - try - GetSelectTableNames(List); - finally - Free; - end; -end; - -procedure ParseUpdateSQL(const SQL: string; var TableName: string; - UpdateFields: TStrings; WhereFields: TStrings); -begin - with TSQLParser.Create(SQL) do - try - GetUpdateTableName(TableName); - if Assigned(UpdateFields) then - begin - Reset; - GetUpdateFields(UpdateFields); - end; - if Assigned(WhereFields) then - begin - Reset; - GetWhereFields(WhereFields); - end; - finally - Free; - end; -end; - -{ TSQLParser } - -constructor TSQLParser.Create(const Text: string); -begin - FText := Text; - FSourcePtr := PChar(Text); - NextToken; -end; - -function TSQLParser.NextToken: TSQLToken; -var - P, TokenStart: PChar; - QuoteChar: Char; - IsParam: Boolean; - - function IsKatakana(const Chr: Byte): Boolean; - begin - Result := (SysLocale.PriLangID = LANG_JAPANESE) and (Chr in [$A1..$DF]); - end; - -begin - if FToken = stEnd then SysUtils.Abort; - FTokenString := ''; - FSymbolQuoted := False; - P := FSourcePtr; - while (P^ <> #0) and (P^ <= ' ') do Inc(P); - FTokenPtr := P; - case P^ of - 'A'..'Z', 'a'..'z', '_', '$', #127..#255: - begin - TokenStart := P; - if not SysLocale.FarEast then - begin - Inc(P); - while CharInSet(P^, ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '"', '$', #127..#255]) do Inc(P); - end - else - begin - while TRUE do - begin - if CharInSet(P^, ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '"', '$']) or - IsKatakana(Byte(P^)) then - Inc(P) - else - if CharInSet(P^, LeadBytes) then - Inc(P, 2) - else - Break; - end; - end; - SetString(FTokenString, TokenStart, P - TokenStart); - FToken := stSymbol; - end; - '''', '"': - begin - QuoteChar := P^; - Inc(P); - IsParam := P^ = ':'; - if IsParam then Inc(P); - TokenStart := P; - while not CharInSet(P^, [QuoteChar, #0]) do Inc(P); - SetString(FTokenString, TokenStart, P - TokenStart); - Inc(P); - Trim(FTokenString); - FToken := stSymbol; - FSymbolQuoted := True; - end; - '-', '0'..'9': - begin - TokenStart := P; - Inc(P); - while CharInSet(P^, ['0'..'9', '.', 'e', 'E', '+', '-']) do Inc(P); - SetString(FTokenString, TokenStart, P - TokenStart); - FToken := stNumber; - end; - ',': - begin - Inc(P); - FToken := stComma; - end; - '=': - begin - Inc(P); - FToken := stEQ; - end; - '(': - begin - Inc(P); - FToken := stLParen; - end; - ')': - begin - Inc(P); - FToken := stRParen; - end; - #0: - FToken := stEnd; - else - begin - FToken := stOther; - Inc(P); - end; - end; - FSourcePtr := P; - if (FToken = stSymbol) and - (FTokenString[Length(FTokenString)] = '.') then FToken := stAlias; - Result := FToken; -end; - -procedure TSQLParser.Reset; -begin - FSourcePtr := PChar(FText); - FToken := stSymbol; - NextToken; -end; - -function TSQLParser.TokenSymbolIs(const S: string): Boolean; -begin - Result := (FToken = stSymbol) and (CompareText(FTokenString, S) = 0); -end; - -procedure TSQLParser.GetSelectTableNames(List: TStrings); -begin - List.BeginUpdate; - try - List.Clear; - if TokenSymbolIs('SELECT') then { Do not localize } - try - while not TokenSymbolIs('FROM') do NextToken; { Do not localize } - NextToken; - while FToken = stSymbol do - begin - List.AddObject(FTokenString, Pointer(Integer(FSymbolQuoted))); - if NextToken = stSymbol then NextToken; - if FToken = stComma then NextToken - else break; - end; - except - end; - finally - List.EndUpdate; - end; -end; - -procedure TSQLParser.GetUpdateTableName(var TableName: string); -begin - if TokenSymbolIs('UPDATE') and (NextToken = stSymbol) then { Do not localize } - TableName := FTokenString else - TableName := ''; -end; - -procedure TSQLParser.GetUpdateFields(List: TStrings); -begin - List.BeginUpdate; - try - List.Clear; - if TokenSymbolIs('UPDATE') then { Do not localize } - try - while not TokenSymbolIs('SET') do NextToken; { Do not localize } - NextToken; - while True do - begin - if FToken = stAlias then NextToken; - if FToken <> stSymbol then Break; - List.Add(FTokenString); - if NextToken <> stEQ then Break; - while NextToken <> stComma do - if TokenSymbolIs('WHERE') then Exit;{ Do not localize } - NextToken; - end; - except - end; - finally - List.EndUpdate; - end; -end; - -procedure TSQLParser.GetWhereFields(List: TStrings); -begin - List.BeginUpdate; - try - List.Clear; - if TokenSymbolIs('UPDATE') then { Do not localize } - try - while not TokenSymbolIs('WHERE') do NextToken; { Do not localize } - NextToken; - while True do - begin - while FToken in [stLParen, stAlias] do NextToken; - if FToken <> stSymbol then Break; - List.Add(FTokenString); - if NextToken <> stEQ then Break; - while true do - begin - NextToken; - if FToken = stEnd then Exit; - if TokenSymbolIs('AND') then Break; { Do not localize } - end; - NextToken; - end; - except - end; - finally - List.EndUpdate; - end; -end; - -{ TUpdateSQLEditor } - -{ Private Methods } - -function TPSQLUpdateSQLEditForm.DatabaseOpen: Boolean; -begin - if Assigned(Database) then - Result := True - else - begin - Result := False; - if not Assigned(DataSet) then Exit; - //New - if not Assigned(DataSet.Database) then Exit; - if Assigned(DataSet.Database) then - begin - Database := DataSet.Database; - Result := True; - end; -// else -// begin -// Database := DataSet.OpenDatabase; -// DatabaseOpened := True; -// Result := True; -// end; - end; -end; - -function TPSQLUpdateSQLEditForm.Edit: Boolean; -var - Index: TUpdateKind; - DataSetName: string; -begin - Result := False; - if Assigned(UpdateSQL.DataSet) and (UpdateSQL.DataSet is TPSQLDataSet) then - begin - DataSet := TPSQLDataSet(UpdateSQL.DataSet); - FTempTable.Database := DataSet.Database; - DataSetName := Format('%s%s%s', [DataSet.Owner.Name, DotSep, DataSet.Name]); - end else - DataSetName := SNoDataSet; - Caption := Format('%s%s%s (%s)', [UpdateSQL.Owner.Name, DotSep, UpdateSQL.Name, DataSetName]); - try - for Index := Low(TUpdateKind) to High(TUpdateKind) do - begin - SQLText[Index] := TStringList.Create; - SQLText[Index].Assign(UpdateSQL.SQL[Index]); - end; - StatementTypeClick(Self); - InitUpdateTableNames; - ShowWait(InitGenerateOptions); - PageControl.ActivePage := PageControl.Pages[0]; - if ShowModal = mrOk then - begin - for Index := low(TUpdateKind) to high(TUpdateKind) do - UpdateSQL.SQL[Index] := SQLText[Index]; - Result := True; - end; - finally - for Index := Low(TUpdateKind) to High(TUpdateKind) do - SQLText[Index].Free; - end; -end; - -procedure TPSQLUpdateSQLEditForm.GenWhereClause(const TabAlias, QuoteChar: string; - KeyFields, SQL: TStrings); -var - I: Integer; - BindText: string; - FieldName: string; -begin - SQL.Add('where'); { Do not localize } - for I := 0 to KeyFields.Count - 1 do - begin - FieldName := KeyFields[I]; - BindText := Format(' %s%s%s%1:s = :%1:s%2:s%1:s', { Do not localize } - [TabAlias, QuoteChar, FieldName]); - if I < KeyFields.Count - 1 then - BindText := Format('%s and',[BindText]); { Do not localize } - SQL.Add(BindText); - end; -end; - -procedure TPSQLUpdateSQLEditForm.GenDeleteSQL(const TableName, QuoteChar: string; - KeyFields, SQL: TStrings); -begin - SQL.Clear; - SQL.Add(Format('delete from %s', [TableName])); { Do not localize } - GenWhereClause(GetTableRef(TableName, QuoteChar), QuoteChar, KeyFields, SQL); -end; - -procedure TPSQLUpdateSQLEditForm.GenInsertSQL(const TableName, QuoteChar: string; - UpdateFields, SQL: TStrings); - - procedure GenFieldList(const TabName, ParamChar, QuoteChar: String); - var - L: string; - I: integer; - Comma: string; - begin - L := ' ('; - Comma := ', '; - for I := 0 to UpdateFields.Count - 1 do - begin - if I = UpdateFields.Count - 1 then Comma := ''; - L := Format('%s%s%s%s%s%3:s%5:s', - [L, TabName, ParamChar, QuoteChar, UpdateFields[I], Comma]); - if (Length(L) > 70) and (I <> UpdateFields.Count - 1) then - begin - SQL.Add(L); - L := ' '; - end; - end; - SQL.Add(L+')'); - end; - -begin - SQL.Clear; - SQL.Add(Format('insert into %s', [TableName])); { Do not localize } - GenFieldList(GetTableRef(TableName, QuoteChar), '', QuoteChar); - SQL.Add('values'); { Do not localize } - GenFieldList('', ':', QuoteChar); -end; - -procedure TPSQLUpdateSQLEditForm.GenModifySQL(const TableName, QuoteChar: string; - KeyFields, UpdateFields, SQL: TStrings); -var - I: integer; - Comma: string; - TableRef: string; -begin - SQL.Clear; - SQL.Add(Format('update %s', [TableName])); { Do not localize } - SQL.Add('set'); { Do not localize } - Comma := ','; - TableRef := GetTableRef(TableName, QuoteChar); - for I := 0 to UpdateFields.Count - 1 do - begin - if I = UpdateFields.Count -1 then Comma := ''; - SQL.Add(Format(' %s%s%s%1:s = :%1:s%2:s%1:s%3:s', - [TableRef, QuoteChar, UpdateFields[I], Comma])); - end; - GenWhereClause(TableRef, QuoteChar, KeyFields, SQL); -end; - -procedure TPSQLUpdateSQLEditForm.GenerateSQL; - - function QuotedTableName(const BaseName: string): string; - begin - with UpdateTableName do - if ((ItemIndex <> -1) and (Items.Objects[ItemIndex] <> nil)) or - (DatabaseOpen and not True and (Pos('.', BaseName) > 0)) then - Result := Format('"%s"', [BaseName]) else - Result := BaseName; - end; - -var - KeyFields: TStringList; - UpdateFields: TStringList; - QuoteChar, TableName: string; -begin - if (KeyFieldList.SelCount = 0) or (UpdateFieldList.SelCount = 0) then - {$IFDEF DELPHI_4} - raise Exception.Create(SSQLGenSelect); - {$ELSE} - raise Exception.CreateRes(@SSQLGenSelect); - {$ENDIF} - KeyFields := TStringList.Create; - try - GetSelectedItems(KeyFieldList, KeyFields); - UpdateFields := TStringList.Create; - try - GetSelectedItems(UpdateFieldList, UpdateFields); - TableName := QuotedTableName(UpdateTableName.Text); - if QuoteFields.Checked then - QuoteChar := '"' else - QuoteChar := ''; - GenDeleteSQL(TableName, QuoteChar, KeyFields, SQLText[ukDelete]); - GenInsertSQL(TableName, QuoteChar, UpdateFields, SQLText[ukInsert]); - GenModifySQL(TableName, QuoteChar, KeyFields, UpdateFields, - SQLText[ukModify]); - SQLMemo.Modified := False; - StatementTypeClick(Self); - PageControl.SelectNextPage(True); - finally - UpdateFields.Free; - end; - finally - KeyFields.Free; - end; -end; - -procedure TPSQLUpdateSQLEditForm.GetDataSetFieldNames; -begin - if Assigned(DataSet) then - begin - GetDataFieldNames(DataSet, DataSet.Name, KeyFieldList.Items); - UpdateFieldList.Items.Assign(KeyFieldList.Items); - end; -end; - -procedure TPSQLUpdateSQLEditForm.GetTableFieldNames; -begin - GetDataFieldNames(TempTable, TempTable.TableName, KeyFieldList.Items); - UpdateFieldList.Items.Assign(KeyFieldList.Items); - FDatasetDefaults := False; -end; - -function TPSQLUpdateSQLEditForm.GetTableRef(const TabName, QuoteChar: string): string; -begin -// if QuoteChar <> '' then -// Result := TabName + '.' else - REsult := ''; -end; - -procedure TPSQLUpdateSQLEditForm.InitGenerateOptions; -var - UpdTabName: string; - - procedure InitFromDataSet; - begin - // If this is a Query with more than 1 table in the "from" clause then - // initialize the list of fields from the table rather than the dataset. - if (UpdateTableName.Items.Count > 1) then - GetTableFieldNames - else - begin - GetDataSetFieldNames; - FDatasetDefaults := True; - end; - SetDefaultSelections; - end; - - procedure InitFromUpdateSQL; - var - UpdFields, - WhFields: TStrings; - begin - UpdFields := TStringList.Create; - try - WhFields := TStringList.Create; - try - ParseUpdateSQL(SQLText[ukModify].Text, UpdTabName, UpdFields, WhFields); - GetDataSetFieldNames; - if SetSelectedItems(UpdateFieldList, UpdFields) < 1 then - SelectAll(UpdateFieldList); - if SetSelectedItems(KeyFieldList, WhFields) < 1 then - SelectAll(KeyFieldList); - finally - WhFields.Free; - end; - finally - UpdFields.Free; - end; - end; - -begin - // If there are existing update SQL statements, try to initialize the - // dialog with the fields that correspond to them. - if SQLText[ukModify].Count > 0 then - begin - ParseUpdateSQL(SQLText[ukModify].Text, UpdTabName, nil, nil); - // If the table name from the update statement is not part of the - // dataset, then initialize from the dataset instead. - if (UpdateTableName.Items.Count > 0) and - (UpdateTableName.Items.IndexOf(UpdTabName) > -1) then - begin - UpdateTableName.Text := UpdTabName; - InitFromUpdateSQL; - end else - begin - InitFromDataSet; - UpdateTableName.Items.Add(UpdTabName); - end; - end else - InitFromDataSet; - SetButtonStates; -end; - -procedure TPSQLUpdateSQLEditForm.InitUpdateTableNames; -begin - UpdateTableName.Items.Clear; - if Assigned(DataSet) then - begin - if DataSet is TPSQLQuery then - GetSQLTableNames(TPSQLQuery(DataSet).SQL.Text, UpdateTableName.Items) - else if (DataSet is TPSQLTable) and (TPSQLTable(DataSet).TableName <> '') then - UpdateTableName.Items.Add(TPSQLTable(DataSet).TableName); - end; - if UpdateTableName.Items.Count > 0 then - UpdateTableName.ItemIndex := 0; -end; - -procedure TPSQLUpdateSQLEditForm.SetButtonStates; -begin - GetTableFieldsButton.Enabled := UpdateTableName.Text <> ''; - PrimaryKeyButton.Enabled := GetTableFieldsButton.Enabled and - (KeyFieldList.Items.Count > 0); - GenerateButton.Enabled := GetTableFieldsButton.Enabled and - (UpdateFieldList.Items.Count > 0) and (KeyFieldList.Items.Count > 0); - DefaultButton.Enabled := Assigned(DataSet) and not FDatasetDefaults; -end; - -procedure TPSQLUpdateSQLEditForm.SelectPrimaryKeyFields; -var - SepPos, I, Index: Integer; - FName, FieldNames: string; -begin - if KeyFieldList.Items.Count < 1 then Exit; - with TempTable do - begin - IndexDefs.Update; - for I := 0 to KeyFieldList.Items.Count - 1 do - KeyFieldList.Selected[I] := False; - for I := 0 to IndexDefs.Count - 1 do - if ixPrimary in IndexDefs[I].Options then - begin - FieldNames := IndexDefs[I].Fields + ';'; - while Length(FieldNames) > 0 do - begin - SepPos := Pos(';', FieldNames); - if SepPos < 1 then Break; - FName := Copy(FieldNames, 1, SepPos - 1); - System.Delete(FieldNames, 1, SepPos); - Index := KeyFieldList.Items.IndexOf(FName); - if Index > -1 then KeyFieldList.Selected[Index] := True; - end; - break; - end; - end; -end; - -procedure TPSQLUpdateSQLEditForm.SetDefaultSelections; -var - DSFields: TStringList; -begin - if FDatasetDefaults or not Assigned(DataSet) then - begin - SelectAll(UpdateFieldList); - SelectAll(KeyFieldList); - end - else if (DataSet.FieldDefs.Count > 0) then - begin - DSFields := TStringList.Create; - try - GetDataFieldNames(DataSet, '', DSFields); - SetSelectedItems(KeyFieldList, DSFields); - SetSelectedItems(UpdateFieldList, DSFields); - finally - DSFields.Free; - end; - end; -end; - -procedure TPSQLUpdateSQLEditForm.ShowWait(WaitMethod: TWaitMethod); -begin - Screen.Cursor := crHourGlass; - try - WaitMethod; - finally - Screen.Cursor := crDefault; - end; -end; - -function TPSQLUpdateSQLEditForm.TempTable: TPSQLTable; -begin - if FTempTable.TableName <> UpdateTableName.Text then - begin - FTempTable.Close; - FTempTable.TableName := UpdateTableName.Text; - end; - Result := FTempTable; -end; - -{ Event Handlers } - -procedure TPSQLUpdateSQLEditForm.FormCreate(Sender: TObject); -begin - {$IFNDEF FPC} - HelpContext := hcDUpdateSQL; - {$ENDIF} - {$WARNINGS OFF} //make D5 happy - FTempTable := TPSQLTable.Create(Self); - {$WARNINGS ON} -end; - -procedure TPSQLUpdateSQLEditForm.HelpButtonClick(Sender: TObject); -begin - Application.HelpContext(HelpContext); -end; - -procedure TPSQLUpdateSQLEditForm.StatementTypeClick(Sender: TObject); -begin - if SQLMemo.Modified then - SQLText[TUpdateKind(StmtIndex)].Assign(SQLMemo.Lines); - StmtIndex := StatementType.ItemIndex; - SQLMemo.Lines.Assign(SQLText[TUpdateKind(StmtIndex)]); -end; - -procedure TPSQLUpdateSQLEditForm.OkButtonClick(Sender: TObject); -begin - if SQLMemo.Modified then - SQLText[TUpdateKind(StmtIndex)].Assign(SQLMemo.Lines); -end; - -procedure TPSQLUpdateSQLEditForm.DefaultButtonClick(Sender: TObject); -begin - with UpdateTableName do - if Items.Count > 0 then ItemIndex := 0; - ShowWait(GetDataSetFieldNames); - FDatasetDefaults := True; - SetDefaultSelections; - KeyfieldList.SetFocus; - SetButtonStates; -end; - -procedure TPSQLUpdateSQLEditForm.GenerateButtonClick(Sender: TObject); -begin - GenerateSQL; - FSettingsChanged := False; -end; - -procedure TPSQLUpdateSQLEditForm.PrimaryKeyButtonClick(Sender: TObject); -begin - ShowWait(SelectPrimaryKeyFields); - SettingsChanged(Sender); -end; - -procedure TPSQLUpdateSQLEditForm.PageControlChanging(Sender: TObject; - var AllowChange: Boolean); -begin - if (PageControl.ActivePage = PageControl.Pages[0]) and - not SQLPage.Enabled then - AllowChange := False; -end; - -procedure TPSQLUpdateSQLEditForm.FormDestroy(Sender: TObject); -begin - if DatabaseOpened then - Database.Close; -end; - -procedure TPSQLUpdateSQLEditForm.GetTableFieldsButtonClick(Sender: TObject); -begin - ShowWait(GetTableFieldNames); - SetDefaultSelections; - SettingsChanged(Sender); -end; - -procedure TPSQLUpdateSQLEditForm.SettingsChanged(Sender: TObject); -begin - FSettingsChanged := True; - FDatasetDefaults := False; - SetButtonStates; -end; - -procedure TPSQLUpdateSQLEditForm.FormCloseQuery(Sender: TObject; - var CanClose: Boolean); -begin - if (ModalResult = mrOK) and FSettingsChanged then - CanClose := MessageDlg(SSQLNotGenerated, mtConfirmation, - mbYesNoCancel, 0) = mrYes; -end; - -procedure TPSQLUpdateSQLEditForm.UpdateTableNameChange(Sender: TObject); -begin - SettingsChanged(Sender); -end; - -procedure TPSQLUpdateSQLEditForm.UpdateTableNameClick(Sender: TObject); -begin - if not Visible then Exit; - GetTableFieldsButtonClick(Sender); -end; - -procedure TPSQLUpdateSQLEditForm.SelectAllClick(Sender: TObject); -begin - SelectAll(FieldListPopup.PopupComponent as TListBox); -end; - -procedure TPSQLUpdateSQLEditForm.ClearAllClick(Sender: TObject); -var - I: Integer; -begin - with FieldListPopup.PopupComponent as TListBox do - begin - Items.BeginUpdate; - try - for I := 0 to Items.Count - 1 do - Selected[I] := False; - finally - Items.EndUpdate; - end; - end; -end; - -procedure TPSQLUpdateSQLEditForm.SQLMemoKeyPress(Sender: TObject; - var Key: Char); -begin - if Key = #27 then Close; -end; - -end. - +{$I psqldac.inc} +unit PSQLupdsqled; + +{SVN revision: $Id$} + +interface + +uses Forms, DB, ExtCtrls, StdCtrls, Controls, ComCtrls, + Classes, SysUtils, Windows, Menus, PSQLDBTables; + +type + + TWaitMethod = procedure of object; + + TPSQLUpdateSQLEditForm = class(TForm) + OkButton: TButton; + CancelButton: TButton; + HelpButton: TButton; + GenerateButton: TButton; + PrimaryKeyButton: TButton; + DefaultButton: TButton; + UpdateTableName: TComboBox; + FieldsPage: TTabSheet; + SQLPage: TTabSheet; + PageControl: TPageControl; + KeyFieldList: TListBox; + UpdateFieldList: TListBox; + GroupBox1: TGroupBox; + Label1: TLabel; + SQLMemo: TMemo; + StatementType: TRadioGroup; + QuoteFields: TCheckBox; + GetTableFieldsButton: TButton; + FieldListPopup: TPopupMenu; + miSelectAll: TMenuItem; + miClearAll: TMenuItem; + procedure FormCreate(Sender: TObject); + procedure HelpButtonClick(Sender: TObject); + procedure StatementTypeClick(Sender: TObject); + procedure OkButtonClick(Sender: TObject); + procedure DefaultButtonClick(Sender: TObject); + procedure GenerateButtonClick(Sender: TObject); + procedure PrimaryKeyButtonClick(Sender: TObject); + procedure PageControlChanging(Sender: TObject; + var AllowChange: Boolean); + procedure FormDestroy(Sender: TObject); + procedure GetTableFieldsButtonClick(Sender: TObject); + procedure SettingsChanged(Sender: TObject); + procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); + procedure UpdateTableNameChange(Sender: TObject); + procedure UpdateTableNameClick(Sender: TObject); + procedure SelectAllClick(Sender: TObject); + procedure ClearAllClick(Sender: TObject); + procedure SQLMemoKeyPress(Sender: TObject; var Key: Char); + private + StmtIndex: Integer; + DataSet: TPSQLDataSet; + Database: TPSQLDatabase; + FTempTable : TPSQLTable; + DatabaseOpened: Boolean; + UpdateSQL: TPSQLUpdateSQL; + FSettingsChanged: Boolean; + FDatasetDefaults: Boolean; + SQLText: array[TUpdateKind] of TStrings; + function GetTableRef(const TabName, QuoteChar: string): string; + function DatabaseOpen: Boolean; + function Edit: Boolean; + procedure GenWhereClause(const TabAlias, QuoteChar: string; + KeyFields, SQL: TStrings); + procedure GenDeleteSQL(const TableName, QuoteChar: string; + KeyFields, SQL: TStrings); + procedure GenInsertSQL(const TableName, QuoteChar: string; + UpdateFields, SQL: TStrings); + procedure GenModifySQL(const TableName, QuoteChar: string; + KeyFields, UpdateFields, SQL: TStrings); + procedure GenerateSQL; + procedure GetDataSetFieldNames; + procedure GetTableFieldNames; + procedure InitGenerateOptions; + procedure InitUpdateTableNames; + procedure SetButtonStates; + procedure SelectPrimaryKeyFields; + procedure SetDefaultSelections; + procedure ShowWait(WaitMethod: TWaitMethod); + function TempTable: TPSQLTable; + end; + +{ TSQLParser } + + TSQLToken = (stSymbol, stAlias, stNumber, stComma, stEQ, stOther, stLParen, + stRParen, stEnd); + + TSQLParser = class + private + FText: string; + FSourcePtr: PChar; + FTokenPtr: PChar; + FTokenString: string; + FToken: TSQLToken; + FSymbolQuoted: Boolean; + function NextToken: TSQLToken; + function TokenSymbolIs(const S: string): Boolean; + procedure Reset; + public + constructor Create(const Text: string); + procedure GetSelectTableNames(List: TStrings); + procedure GetUpdateTableName(var TableName: string); + procedure GetUpdateFields(List: TStrings); + procedure GetWhereFields(List: TStrings); + end; + +function EditPSQLUpdateSQL(AUpdateSQL: TPSQLUpdateSQL): Boolean; + +implementation + +{$R *.DFM} + +uses Dialogs, {$IFNDEF FPC}LibHelp,{$ENDIF} TypInfo, PSQLTypes; + +{ Global Interface functions } + +function EditPSQLUpdateSQL(AUpdateSQL: TPSQLUpdateSQL): Boolean; +begin + with TPSQLUpdateSQLEditForm.Create(Application) do + try + UpdateSQL := AUpdateSQL; + Result := Edit; + finally + Free; + end; +end; + +{ Utility Routines } + +procedure GetSelectedItems(ListBox: TListBox; List: TStrings); +var + I: Integer; +begin + List.Clear; + for I := 0 to ListBox.Items.Count - 1 do + if ListBox.Selected[I] then + List.Add(ListBox.Items[I]); +end; + +function SetSelectedItems(ListBox: TListBox; List: TStrings): Integer; +var + I: Integer; +begin + Result := 0; + ListBox.Items.BeginUpdate; + try + for I := 0 to ListBox.Items.Count - 1 do + if List.IndexOf(ListBox.Items[I]) > -1 then + begin + ListBox.Selected[I] := True; + Inc(Result); + end + else + ListBox.Selected[I] := False; + if ListBox.Items.Count > 0 then + begin + ListBox.ItemIndex := 0; + ListBox.TopIndex := 0; + end; + finally + ListBox.Items.EndUpdate; + end; +end; + +procedure SelectAll(ListBox: TListBox); +var + I: Integer; +begin + ListBox.Items.BeginUpdate; + try + with ListBox do + for I := 0 to Items.Count - 1 do + Selected[I] := True; + if ListBox.Items.Count > 0 then + begin + ListBox.ItemIndex := 0; + ListBox.TopIndex := 0; + end; + finally + ListBox.Items.EndUpdate; + end; +end; + +procedure GetDataFieldNames(Dataset: TDataset; ErrorName: string; List: TStrings); +var + I: Integer; +begin + with Dataset do + try + FieldDefs.Update; + List.BeginUpdate; + try + List.Clear; + for I := 0 to FieldDefs.Count - 1 do + List.Add(FieldDefs[I].Name); + finally + List.EndUpdate; + end; + except + if ErrorName <> '' then + MessageDlg(Format(SSQLDataSetOpen, [ErrorName]), mtError, [mbOK], 0); + end; +end; + +procedure GetSQLTableNames(const SQL: string; List: TStrings); +begin + with TSQLParser.Create(SQL) do + try + GetSelectTableNames(List); + finally + Free; + end; +end; + +procedure ParseUpdateSQL(const SQL: string; var TableName: string; + UpdateFields: TStrings; WhereFields: TStrings); +begin + with TSQLParser.Create(SQL) do + try + GetUpdateTableName(TableName); + if Assigned(UpdateFields) then + begin + Reset; + GetUpdateFields(UpdateFields); + end; + if Assigned(WhereFields) then + begin + Reset; + GetWhereFields(WhereFields); + end; + finally + Free; + end; +end; + +{ TSQLParser } + +constructor TSQLParser.Create(const Text: string); +begin + FText := Text; + FSourcePtr := PChar(Text); + NextToken; +end; + +function TSQLParser.NextToken: TSQLToken; +var + P, TokenStart: PChar; + QuoteChar: Char; + IsParam: Boolean; + + function IsKatakana(const Chr: Byte): Boolean; + begin + Result := (SysLocale.PriLangID = LANG_JAPANESE) and (Chr in [$A1..$DF]); + end; + +begin + if FToken = stEnd then SysUtils.Abort; + FTokenString := ''; + FSymbolQuoted := False; + P := FSourcePtr; + while (P^ <> #0) and (P^ <= ' ') do Inc(P); + FTokenPtr := P; + case P^ of + 'A'..'Z', 'a'..'z', '_', '$', #127..#255: + begin + TokenStart := P; + if not SysLocale.FarEast then + begin + Inc(P); + while CharInSet(P^, ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '"', '$', #127..#255]) do Inc(P); + end + else + begin + while TRUE do + begin + if CharInSet(P^, ['A'..'Z', 'a'..'z', '0'..'9', '_', '.', '"', '$']) or + IsKatakana(Byte(P^)) then + Inc(P) + else + if CharInSet(P^, LeadBytes) then + Inc(P, 2) + else + Break; + end; + end; + SetString(FTokenString, TokenStart, P - TokenStart); + FToken := stSymbol; + end; + '''', '"': + begin + QuoteChar := P^; + Inc(P); + IsParam := P^ = ':'; + if IsParam then Inc(P); + TokenStart := P; + while not CharInSet(P^, [QuoteChar, #0]) do Inc(P); + SetString(FTokenString, TokenStart, P - TokenStart); + Inc(P); + Trim(FTokenString); + FToken := stSymbol; + FSymbolQuoted := True; + end; + '-', '0'..'9': + begin + TokenStart := P; + Inc(P); + while CharInSet(P^, ['0'..'9', '.', 'e', 'E', '+', '-']) do Inc(P); + SetString(FTokenString, TokenStart, P - TokenStart); + FToken := stNumber; + end; + ',': + begin + Inc(P); + FToken := stComma; + end; + '=': + begin + Inc(P); + FToken := stEQ; + end; + '(': + begin + Inc(P); + FToken := stLParen; + end; + ')': + begin + Inc(P); + FToken := stRParen; + end; + #0: + FToken := stEnd; + else + begin + FToken := stOther; + Inc(P); + end; + end; + FSourcePtr := P; + if (FToken = stSymbol) and + (FTokenString[Length(FTokenString)] = '.') then FToken := stAlias; + Result := FToken; +end; + +procedure TSQLParser.Reset; +begin + FSourcePtr := PChar(FText); + FToken := stSymbol; + NextToken; +end; + +function TSQLParser.TokenSymbolIs(const S: string): Boolean; +begin + Result := (FToken = stSymbol) and (CompareText(FTokenString, S) = 0); +end; + +procedure TSQLParser.GetSelectTableNames(List: TStrings); +begin + List.BeginUpdate; + try + List.Clear; + if TokenSymbolIs('SELECT') then { Do not localize } + try + while not TokenSymbolIs('FROM') do NextToken; { Do not localize } + NextToken; + while FToken = stSymbol do + begin + List.AddObject(FTokenString, Pointer(Integer(FSymbolQuoted))); + if NextToken = stSymbol then NextToken; + if FToken = stComma then NextToken + else break; + end; + except + end; + finally + List.EndUpdate; + end; +end; + +procedure TSQLParser.GetUpdateTableName(var TableName: string); +begin + if TokenSymbolIs('UPDATE') and (NextToken = stSymbol) then { Do not localize } + TableName := FTokenString else + TableName := ''; +end; + +procedure TSQLParser.GetUpdateFields(List: TStrings); +begin + List.BeginUpdate; + try + List.Clear; + if TokenSymbolIs('UPDATE') then { Do not localize } + try + while not TokenSymbolIs('SET') do NextToken; { Do not localize } + NextToken; + while True do + begin + if FToken = stAlias then NextToken; + if FToken <> stSymbol then Break; + List.Add(FTokenString); + if NextToken <> stEQ then Break; + while NextToken <> stComma do + if TokenSymbolIs('WHERE') then Exit;{ Do not localize } + NextToken; + end; + except + end; + finally + List.EndUpdate; + end; +end; + +procedure TSQLParser.GetWhereFields(List: TStrings); +begin + List.BeginUpdate; + try + List.Clear; + if TokenSymbolIs('UPDATE') then { Do not localize } + try + while not TokenSymbolIs('WHERE') do NextToken; { Do not localize } + NextToken; + while True do + begin + while FToken in [stLParen, stAlias] do NextToken; + if FToken <> stSymbol then Break; + List.Add(FTokenString); + if NextToken <> stEQ then Break; + while true do + begin + NextToken; + if FToken = stEnd then Exit; + if TokenSymbolIs('AND') then Break; { Do not localize } + end; + NextToken; + end; + except + end; + finally + List.EndUpdate; + end; +end; + +{ TUpdateSQLEditor } + +{ Private Methods } + +function TPSQLUpdateSQLEditForm.DatabaseOpen: Boolean; +begin + if Assigned(Database) then + Result := True + else + begin + Result := False; + if not Assigned(DataSet) then Exit; + //New + if not Assigned(DataSet.Database) then Exit; + if Assigned(DataSet.Database) then + begin + Database := DataSet.Database; + Result := True; + end; +// else +// begin +// Database := DataSet.OpenDatabase; +// DatabaseOpened := True; +// Result := True; +// end; + end; +end; + +function TPSQLUpdateSQLEditForm.Edit: Boolean; +var + Index: TUpdateKind; + DataSetName: string; +begin + Result := False; + if Assigned(UpdateSQL.DataSet) and (UpdateSQL.DataSet is TPSQLDataSet) then + begin + DataSet := TPSQLDataSet(UpdateSQL.DataSet); + FTempTable.Database := DataSet.Database; + DataSetName := Format('%s%s%s', [DataSet.Owner.Name, DotSep, DataSet.Name]); + end else + DataSetName := SNoDataSet; + Caption := Format('%s%s%s (%s)', [UpdateSQL.Owner.Name, DotSep, UpdateSQL.Name, DataSetName]); + try + for Index := Low(TUpdateKind) to High(TUpdateKind) do + begin + SQLText[Index] := TStringList.Create; + SQLText[Index].Assign(UpdateSQL.SQL[Index]); + end; + StatementTypeClick(Self); + InitUpdateTableNames; + ShowWait(InitGenerateOptions); + PageControl.ActivePage := PageControl.Pages[0]; + if ShowModal = mrOk then + begin + for Index := low(TUpdateKind) to high(TUpdateKind) do + UpdateSQL.SQL[Index] := SQLText[Index]; + Result := True; + end; + finally + for Index := Low(TUpdateKind) to High(TUpdateKind) do + SQLText[Index].Free; + end; +end; + +procedure TPSQLUpdateSQLEditForm.GenWhereClause(const TabAlias, QuoteChar: string; + KeyFields, SQL: TStrings); +var + I: Integer; + BindText: string; + FieldName: string; +begin + SQL.Add('where'); { Do not localize } + for I := 0 to KeyFields.Count - 1 do + begin + FieldName := KeyFields[I]; + BindText := Format(' %s%s%s%1:s = :%1:s%2:s%1:s', { Do not localize } + [TabAlias, QuoteChar, FieldName]); + if I < KeyFields.Count - 1 then + BindText := Format('%s and',[BindText]); { Do not localize } + SQL.Add(BindText); + end; +end; + +procedure TPSQLUpdateSQLEditForm.GenDeleteSQL(const TableName, QuoteChar: string; + KeyFields, SQL: TStrings); +begin + SQL.Clear; + SQL.Add(Format('delete from %s', [TableName])); { Do not localize } + GenWhereClause(GetTableRef(TableName, QuoteChar), QuoteChar, KeyFields, SQL); +end; + +procedure TPSQLUpdateSQLEditForm.GenInsertSQL(const TableName, QuoteChar: string; + UpdateFields, SQL: TStrings); + + procedure GenFieldList(const TabName, ParamChar, QuoteChar: String); + var + L: string; + I: integer; + Comma: string; + begin + L := ' ('; + Comma := ', '; + for I := 0 to UpdateFields.Count - 1 do + begin + if I = UpdateFields.Count - 1 then Comma := ''; + L := Format('%s%s%s%s%s%3:s%5:s', + [L, TabName, ParamChar, QuoteChar, UpdateFields[I], Comma]); + if (Length(L) > 70) and (I <> UpdateFields.Count - 1) then + begin + SQL.Add(L); + L := ' '; + end; + end; + SQL.Add(L+')'); + end; + +begin + SQL.Clear; + SQL.Add(Format('insert into %s', [TableName])); { Do not localize } + GenFieldList(GetTableRef(TableName, QuoteChar), '', QuoteChar); + SQL.Add('values'); { Do not localize } + GenFieldList('', ':', QuoteChar); +end; + +procedure TPSQLUpdateSQLEditForm.GenModifySQL(const TableName, QuoteChar: string; + KeyFields, UpdateFields, SQL: TStrings); +var + I: integer; + Comma: string; + TableRef: string; +begin + SQL.Clear; + SQL.Add(Format('update %s', [TableName])); { Do not localize } + SQL.Add('set'); { Do not localize } + Comma := ','; + TableRef := GetTableRef(TableName, QuoteChar); + for I := 0 to UpdateFields.Count - 1 do + begin + if I = UpdateFields.Count -1 then Comma := ''; + SQL.Add(Format(' %s%s%s%1:s = :%1:s%2:s%1:s%3:s', + [TableRef, QuoteChar, UpdateFields[I], Comma])); + end; + GenWhereClause(TableRef, QuoteChar, KeyFields, SQL); +end; + +procedure TPSQLUpdateSQLEditForm.GenerateSQL; + + function QuotedTableName(const BaseName: string): string; + begin + with UpdateTableName do + if ((ItemIndex <> -1) and (Items.Objects[ItemIndex] <> nil)) or + (DatabaseOpen and not True and (Pos('.', BaseName) > 0)) then + Result := Format('"%s"', [BaseName]) else + Result := BaseName; + end; + +var + KeyFields: TStringList; + UpdateFields: TStringList; + QuoteChar, TableName: string; +begin + if (KeyFieldList.SelCount = 0) or (UpdateFieldList.SelCount = 0) then + {$IFDEF DELPHI_4} + raise Exception.Create(SSQLGenSelect); + {$ELSE} + raise Exception.CreateRes(@SSQLGenSelect); + {$ENDIF} + KeyFields := TStringList.Create; + try + GetSelectedItems(KeyFieldList, KeyFields); + UpdateFields := TStringList.Create; + try + GetSelectedItems(UpdateFieldList, UpdateFields); + TableName := QuotedTableName(UpdateTableName.Text); + if QuoteFields.Checked then + QuoteChar := '"' else + QuoteChar := ''; + GenDeleteSQL(TableName, QuoteChar, KeyFields, SQLText[ukDelete]); + GenInsertSQL(TableName, QuoteChar, UpdateFields, SQLText[ukInsert]); + GenModifySQL(TableName, QuoteChar, KeyFields, UpdateFields, + SQLText[ukModify]); + SQLMemo.Modified := False; + StatementTypeClick(Self); + PageControl.SelectNextPage(True); + finally + UpdateFields.Free; + end; + finally + KeyFields.Free; + end; +end; + +procedure TPSQLUpdateSQLEditForm.GetDataSetFieldNames; +begin + if Assigned(DataSet) then + begin + GetDataFieldNames(DataSet, DataSet.Name, KeyFieldList.Items); + UpdateFieldList.Items.Assign(KeyFieldList.Items); + end; +end; + +procedure TPSQLUpdateSQLEditForm.GetTableFieldNames; +begin + GetDataFieldNames(TempTable, TempTable.TableName, KeyFieldList.Items); + UpdateFieldList.Items.Assign(KeyFieldList.Items); + FDatasetDefaults := False; +end; + +function TPSQLUpdateSQLEditForm.GetTableRef(const TabName, QuoteChar: string): string; +begin +// if QuoteChar <> '' then +// Result := TabName + '.' else + REsult := ''; +end; + +procedure TPSQLUpdateSQLEditForm.InitGenerateOptions; +var + UpdTabName: string; + + procedure InitFromDataSet; + begin + // If this is a Query with more than 1 table in the "from" clause then + // initialize the list of fields from the table rather than the dataset. + if (UpdateTableName.Items.Count > 1) then + GetTableFieldNames + else + begin + GetDataSetFieldNames; + FDatasetDefaults := True; + end; + SetDefaultSelections; + end; + + procedure InitFromUpdateSQL; + var + UpdFields, + WhFields: TStrings; + begin + UpdFields := TStringList.Create; + try + WhFields := TStringList.Create; + try + ParseUpdateSQL(SQLText[ukModify].Text, UpdTabName, UpdFields, WhFields); + GetDataSetFieldNames; + if SetSelectedItems(UpdateFieldList, UpdFields) < 1 then + SelectAll(UpdateFieldList); + if SetSelectedItems(KeyFieldList, WhFields) < 1 then + SelectAll(KeyFieldList); + finally + WhFields.Free; + end; + finally + UpdFields.Free; + end; + end; + +begin + // If there are existing update SQL statements, try to initialize the + // dialog with the fields that correspond to them. + if SQLText[ukModify].Count > 0 then + begin + ParseUpdateSQL(SQLText[ukModify].Text, UpdTabName, nil, nil); + // If the table name from the update statement is not part of the + // dataset, then initialize from the dataset instead. + if (UpdateTableName.Items.Count > 0) and + (UpdateTableName.Items.IndexOf(UpdTabName) > -1) then + begin + UpdateTableName.Text := UpdTabName; + InitFromUpdateSQL; + end else + begin + InitFromDataSet; + UpdateTableName.Items.Add(UpdTabName); + end; + end else + InitFromDataSet; + SetButtonStates; +end; + +procedure TPSQLUpdateSQLEditForm.InitUpdateTableNames; +begin + UpdateTableName.Items.Clear; + if Assigned(DataSet) then + begin + if DataSet is TPSQLQuery then + GetSQLTableNames(TPSQLQuery(DataSet).SQL.Text, UpdateTableName.Items) + else if (DataSet is TPSQLTable) and (TPSQLTable(DataSet).TableName <> '') then + UpdateTableName.Items.Add(TPSQLTable(DataSet).TableName); + end; + if UpdateTableName.Items.Count > 0 then + UpdateTableName.ItemIndex := 0; +end; + +procedure TPSQLUpdateSQLEditForm.SetButtonStates; +begin + GetTableFieldsButton.Enabled := UpdateTableName.Text <> ''; + PrimaryKeyButton.Enabled := GetTableFieldsButton.Enabled and + (KeyFieldList.Items.Count > 0); + GenerateButton.Enabled := GetTableFieldsButton.Enabled and + (UpdateFieldList.Items.Count > 0) and (KeyFieldList.Items.Count > 0); + DefaultButton.Enabled := Assigned(DataSet) and not FDatasetDefaults; +end; + +procedure TPSQLUpdateSQLEditForm.SelectPrimaryKeyFields; +var + SepPos, I, Index: Integer; + FName, FieldNames: string; +begin + if KeyFieldList.Items.Count < 1 then Exit; + with TempTable do + begin + IndexDefs.Update; + for I := 0 to KeyFieldList.Items.Count - 1 do + KeyFieldList.Selected[I] := False; + for I := 0 to IndexDefs.Count - 1 do + if ixPrimary in IndexDefs[I].Options then + begin + FieldNames := IndexDefs[I].Fields + ';'; + while Length(FieldNames) > 0 do + begin + SepPos := Pos(';', FieldNames); + if SepPos < 1 then Break; + FName := Copy(FieldNames, 1, SepPos - 1); + System.Delete(FieldNames, 1, SepPos); + Index := KeyFieldList.Items.IndexOf(FName); + if Index > -1 then KeyFieldList.Selected[Index] := True; + end; + break; + end; + end; +end; + +procedure TPSQLUpdateSQLEditForm.SetDefaultSelections; +var + DSFields: TStringList; +begin + if FDatasetDefaults or not Assigned(DataSet) then + begin + SelectAll(UpdateFieldList); + SelectAll(KeyFieldList); + end + else if (DataSet.FieldDefs.Count > 0) then + begin + DSFields := TStringList.Create; + try + GetDataFieldNames(DataSet, '', DSFields); + SetSelectedItems(KeyFieldList, DSFields); + SetSelectedItems(UpdateFieldList, DSFields); + finally + DSFields.Free; + end; + end; +end; + +procedure TPSQLUpdateSQLEditForm.ShowWait(WaitMethod: TWaitMethod); +begin + Screen.Cursor := crHourGlass; + try + WaitMethod; + finally + Screen.Cursor := crDefault; + end; +end; + +function TPSQLUpdateSQLEditForm.TempTable: TPSQLTable; +begin + if FTempTable.TableName <> UpdateTableName.Text then + begin + FTempTable.Close; + FTempTable.TableName := UpdateTableName.Text; + end; + Result := FTempTable; +end; + +{ Event Handlers } + +procedure TPSQLUpdateSQLEditForm.FormCreate(Sender: TObject); +begin + {$IFNDEF FPC} + HelpContext := hcDUpdateSQL; + {$ENDIF} + {$WARNINGS OFF} //make D5 happy + FTempTable := TPSQLTable.Create(Self); + {$WARNINGS ON} +end; + +procedure TPSQLUpdateSQLEditForm.HelpButtonClick(Sender: TObject); +begin + Application.HelpContext(HelpContext); +end; + +procedure TPSQLUpdateSQLEditForm.StatementTypeClick(Sender: TObject); +begin + if SQLMemo.Modified then + SQLText[TUpdateKind(StmtIndex)].Assign(SQLMemo.Lines); + StmtIndex := StatementType.ItemIndex; + SQLMemo.Lines.Assign(SQLText[TUpdateKind(StmtIndex)]); +end; + +procedure TPSQLUpdateSQLEditForm.OkButtonClick(Sender: TObject); +begin + if SQLMemo.Modified then + SQLText[TUpdateKind(StmtIndex)].Assign(SQLMemo.Lines); +end; + +procedure TPSQLUpdateSQLEditForm.DefaultButtonClick(Sender: TObject); +begin + with UpdateTableName do + if Items.Count > 0 then ItemIndex := 0; + ShowWait(GetDataSetFieldNames); + FDatasetDefaults := True; + SetDefaultSelections; + KeyfieldList.SetFocus; + SetButtonStates; +end; + +procedure TPSQLUpdateSQLEditForm.GenerateButtonClick(Sender: TObject); +begin + GenerateSQL; + FSettingsChanged := False; +end; + +procedure TPSQLUpdateSQLEditForm.PrimaryKeyButtonClick(Sender: TObject); +begin + ShowWait(SelectPrimaryKeyFields); + SettingsChanged(Sender); +end; + +procedure TPSQLUpdateSQLEditForm.PageControlChanging(Sender: TObject; + var AllowChange: Boolean); +begin + if (PageControl.ActivePage = PageControl.Pages[0]) and + not SQLPage.Enabled then + AllowChange := False; +end; + +procedure TPSQLUpdateSQLEditForm.FormDestroy(Sender: TObject); +begin + if DatabaseOpened then + Database.Close; +end; + +procedure TPSQLUpdateSQLEditForm.GetTableFieldsButtonClick(Sender: TObject); +begin + ShowWait(GetTableFieldNames); + SetDefaultSelections; + SettingsChanged(Sender); +end; + +procedure TPSQLUpdateSQLEditForm.SettingsChanged(Sender: TObject); +begin + FSettingsChanged := True; + FDatasetDefaults := False; + SetButtonStates; +end; + +procedure TPSQLUpdateSQLEditForm.FormCloseQuery(Sender: TObject; + var CanClose: Boolean); +begin + if (ModalResult = mrOK) and FSettingsChanged then + CanClose := MessageDlg(SSQLNotGenerated, mtConfirmation, + mbYesNoCancel, 0) = mrYes; +end; + +procedure TPSQLUpdateSQLEditForm.UpdateTableNameChange(Sender: TObject); +begin + SettingsChanged(Sender); +end; + +procedure TPSQLUpdateSQLEditForm.UpdateTableNameClick(Sender: TObject); +begin + if not Visible then Exit; + GetTableFieldsButtonClick(Sender); +end; + +procedure TPSQLUpdateSQLEditForm.SelectAllClick(Sender: TObject); +begin + SelectAll(FieldListPopup.PopupComponent as TListBox); +end; + +procedure TPSQLUpdateSQLEditForm.ClearAllClick(Sender: TObject); +var + I: Integer; +begin + with FieldListPopup.PopupComponent as TListBox do + begin + Items.BeginUpdate; + try + for I := 0 to Items.Count - 1 do + Selected[I] := False; + finally + Items.EndUpdate; + end; + end; +end; + +procedure TPSQLUpdateSQLEditForm.SQLMemoKeyPress(Sender: TObject; + var Key: Char); +begin + if Key = #27 then Close; +end; + +end. + diff --git a/psqlAboutFrm.dfm b/Source/psqlAboutFrm.dfm similarity index 98% rename from psqlAboutFrm.dfm rename to Source/psqlAboutFrm.dfm index fc69a5b..e3eedc7 100644 --- a/psqlAboutFrm.dfm +++ b/Source/psqlAboutFrm.dfm @@ -1,1595 +1,1595 @@ -object PSQLAboutComp: TPSQLAboutComp - Left = 411 - Top = 251 - BorderIcons = [biSystemMenu] - BorderStyle = bsDialog - Caption = 'About...' - ClientHeight = 278 - ClientWidth = 287 - Color = clBtnFace - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - OldCreateOrder = True - Position = poScreenCenter - OnCreate = FormCreate - PixelsPerInch = 96 - TextHeight = 13 - object Bevel1: TBevel - Left = 8 - Top = 8 - Width = 273 - Height = 233 - Shape = bsFrame - end - object Image1: TImage - Left = 13 - Top = 14 - Width = 260 - Height = 59 - AutoSize = True - Center = True - Picture.Data = { - 07544269746D6170FAB30000424DFAB300000000000036000000280000000401 - 00003B0000000100180000000000C4B30000120B0000120B0000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFBF5EFD59BF2DFB1FFFFFEFF - FFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFF - BBBBBBBBBBBBFFFFFFEEEEEEBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFEEEEEE8888 - 88888888888888EEEEEEFFFFFFFFFFFFCCCCCCBBBBBBEEEEEEFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFF999999888888888888DDDDDDFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFEEEEEE999999888888888888999999EEEEEEFFFFFFFFFFFFFFFF - FFFFFFFFDDDDDDBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBDD - DDDDBBBBBBBBBBBBDDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCBBBBBB - BBBBBBEEEEEEFFFFFFBBBBBBBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7EDD5E3 - BA5EEED396FDFBF4FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000 - 000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFF - FFFFFFFFCCCCCC111111000000000000000000111111CCCCCCFFFFFF44444400 - 0000BBBBBBFFFFFFFFFFFFFFFFFFEEEEEE333333000000000000000000000000 - 999999FFFFFFFFFFFFFFFFFFFFFFFF9999991111110000000000000000000000 - 00111111999999FFFFFFFFFFFFFFFFFF88888800000000000000000000000000 - 0000000000000000000000999999000000000000666666FFFFFFFFFFFFFFFFFF - FFFFFFFFFFFF222222000000000000DDDDDDFFFFFF000000000000444444FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFF1DDB1D9A52EF0DBAAFFFFFFFFFFFFFFFFFFFFFFFF000000000000 - FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFF - FFBBBBBB000000444444FFFFFFFFFFFF33333300000022222288888811111100 - 0000444444FFFFFF444444000000BBBBBBFFFFFFFFFFFFFFFFFF777777000000 - 000000777777333333000000111111DDDDDDFFFFFFFFFFFFBBBBBB0000000000 - 00000000000000000000000000000000000000BBBBBBFFFFFFFFFFFF88888800 - 0000000000000000000000000000000000000000000000EEEEEE000000000000 - 111111FFFFFFFFFFFFFFFFFFFFFFFFCCCCCC000000000000333333FFFFFFFFFF - FF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFECD29BD2940CEFDAA9FFFFFFFFFFFFFFFFFF - FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFF - FFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFFCCCCCC00000011 - 1111DDDDDDFFFFFFDDDDDD000000000000DDDDDD444444000000BBBBBBFFFFFF - FFFFFFFFFFFF111111000000888888FFFFFFEEEEEE222222000000888888FFFF - FFFFFFFF22222200000000000033333399999999999933333300000000000022 - 2222FFFFFFFFFFFF888888000000000000666666888888888888888888888888 - 888888FFFFFF555555000000000000CCCCCCFFFFFFFFFFFFFFFFFF8888880000 - 00000000777777FFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDD5A1CF8D00E7C57C - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFF - FF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB00000044 - 4444FFFFFF888888000000555555FFFFFFFFFFFFFFFFFF777777666666DDDDDD - 444444000000BBBBBBFFFFFFFFFFFFCCCCCC000000111111FFFFFFFFFFFFFFFF - FF888888000000555555FFFFFFAAAAAA000000000000222222EEEEEEFFFFFFFF - FFFFEEEEEE222222000000000000AAAAAAFFFFFF888888000000000000BBBBBB - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8888880000000000000000000000 - 00000000000000000000000000000000CCCCCCFFFFFFFFFFFF00000000000044 - 4444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - F0DCB2CE8F07DAA93CFEFCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000 - 00000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF00000000 - 0000FFFFFFBBBBBB000000444444FFFFFF888888000000888888FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB0000 - 00444444FFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFF88888800000000 - 0000999999FFFFFFFFFFFFFFFFFFFFFFFF999999000000000000888888FFFFFF - 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDD - DD000000000000000000000000000000000000000000000000111111FFFFFFFF - FFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFF7EDD8D1961ACE8C01F3E2BEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FF - FFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFF888888 - 000000888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBB - BBFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFFFFFFFFBBBBBB00000044 - 4444FFFFFF444444000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD - 000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFF33333300000000000033333344444422222200 - 0000000000666666FFFFFFFFFFFFFFFFFF000000000000111111444444444444 - 444444777777DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFEFCF8DAAB4CCC8A00DBAB44FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFAFDFAFFFFFFFFFFFF000000000000EEEEEEFF - FFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB - 000000444444FFFFFF888888000000666666FFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB000000222222FFFFFFFF - FFFFFFFFFF999999000000444444FFFFFF444444000000000000FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFF8888880000000000 - 00BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800000000 - 0000999999FFFFFF666666000000000000999999FFFFFFFFFFFFFFFFFF000000 - 000000000000000000000000000000000000111111CCCCCCFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDD7 - AACB8800CC8A00EACE93FFFFFFFFFFFFFFFFFFFDFEFDC4E8C9CCEBD1FEFEFEFF - FFFF000000000000999999FFFFFFDDDDDD000000000000AAAAAAFFFFFFEEEEEE - 000000000000FFFFFFBBBBBB000000444444FFFFFFBBBBBB000000222222EEEE - EEFFFFFFDDDDDD000000333333FFFFFF444444000000777777FFFFFFFFFFFFFF - FFFF000000000000BBBBBBFFFFFFFFFFFF444444000000888888FFFFFF444444 - 000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000004444 - 44FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFCCCCCC000000000000555555FFFFFF111111000000000000EEEEEE - FFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000 - 00222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFEFDFAD5A33BCB8800CD8D07F5E6C9FFFFFFFFFFFFEBF7EC9B - D7A1B6E2BCF5FBF6FFFFFFFFFFFF000000000000111111999999666666000000 - 000000111111AAAAAA555555000000222222FFFFFFBBBBBB000000444444FFFF - FFFFFFFF111111000000555555BBBBBB333333000000555555FFFFFF44444400 - 0000111111999999888888FFFFFF555555000000111111AAAAAA666666000000 - 000000CCCCCCFFFFFF444444000000000000EEEEEEFFFFFFFFFFFFFFFFFFFFFF - FFEEEEEE000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF222222000000111111CCCCCC - 000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFF - FFFFFFFFBBBBBB111111000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF1E1C0CA8904CB8800D4951AF8 - F1E0FFFFFFD4EED67AC97DBDE4C0FFFFFFFFFFFFFFFFFFFFFFFF000000000000 - 111111000000000000000000555555000000000000000000000000777777FFFF - FFBBBBBB000000444444FFFFFFFFFFFF99999900000000000000000000000000 - 0000BBBBBBFFFFFF444444000000000000000000000000DDDDDDDDDDDD111111 - 000000000000000000000000777777FFFFFFFFFFFF777777000000000000AAAA - AAFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000777777FFFFFF88888800 - 0000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - 777777000000000000444444000000000000888888FFFFFFFFFFFFFFFFFFFFFF - FF000000000000444444FFFFFFFFFFFFFFFFFF777777000000000000BBBBBBFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFE3 - C07CC98700CA8800DC9723FBFBF6C6EACB62BE62BEE4BFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFF888888888888CCCCCC555555444444888888FFFFFFAAAAAA4444 - 44444444888888FFFFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFBB - BBBB555555444444555555BBBBBBFFFFFFFFFFFF999999888888888888444444 - 666666EEEEEEFFFFFFDDDDDD666666444444444444999999FFFFFFFFFFFFFFFF - FF999999000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF44444400000000 - 0000999999FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000000000000000000000000000CCCC - CCFFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFF66 - 6666000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFD8A84AC88700D08703F0D4A4D3E8C752BD59A2D9A3 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEE111111000000000000666666DDDDDDEE - EEEE666666000000000000111111EEEEEEFFFFFF888888000000000000BBBBBB - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1111110000 - 00000000000000222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000033 - 3333BBBBBBBBBBBB888888000000000000000000DDDDDDFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD09827CB8600E5C685 - E6EDD55BBF5D77C573F8FCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800 - 0000000000000000000000000000000000000000000000888888FFFFFFFFFFFF - 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF666666000000000000000000666666FFFFFFFFFFFFFFFFFFFF - FFFFFFFFFF000000000000000000000000000000000000000000000000444444 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFCB8C0FE0A03EF1F2E269C66A54B94ECDECCEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF666666000000000000000000000000000000000000 - 777777FFFFFFFFFFFFFFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000000000AA - AAAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000 - 000000000000111111CCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFD69424EBE3C39BD18851B84884C877FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFBFDFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD - 888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB666666 - 444444444444666666BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB8888888888 - 88DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFF888888888888888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF888888 - 888888888888888888888888888888999999EEEEEEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFEBD09ED3E5 - BF4FBB484FBA4CABC16CF9E9CFFFFFFFFFFFFFFFFFFFC7DAFAC9DCFBFEFEFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFEFDFB7FCA7053B7415DBE57B7AF4AEBAC47FFFFFFF0F5FF9A - BBF7A8C6F8F1F6FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFCFEFCF9FFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFDEECCF54B73E53B7416ABF5ECD - B048ECCE99E5E7ED6C9FFFA9C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0DCAFDAA83ADE - B152E3BE70E8C98BEDD5A5F1E0BEF7ECD8FBF5EBFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA9 - DB9A56B53953B6426AC252F2EBCFE0E1EA4B8AFFBAC5DBFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFE3BD68CF8E00CE8C00CD8B00CC8900CB8800CB8803CA8909DAAE58 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFF89CB6F55B53955B73DBCE2B1DCE6EE447FFF8B9EC8 - E1B354F8E5C1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF8EFD7A027CF8E00CE8C00CD8B00CC8900 - CB8800CA8700C88500E9CE9AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF70C05055B637A4D996 - E1EBF34A7CFF688FE3CEA95CDA9200D79C17F2DEAFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECD093D08F00 - CF8E00CE8C00CD8B00CC8900CB8800CA8700D6A440FDFBF6FFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFF60B93F79C857E8F3EC658AF63C71FCAFA495DB9409D19100D39200D698 - 08EBCD85FEFCF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFAF4E5D79D1FD08F00CF8E00CE8C00CD8B00CC8900CB8800CD8E0EF3E5 - C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFF6ABF40CFE8CE94AEF03E6DFA7A9FEBD9C0 - 84D88F00D19100D39200D59500D69700E3B548F8EBCDFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3B95CD19000D08F00CF8E00CE8C00CD8B - 00CC8900CA8801EBD2A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFB8E0A6CADC - E5446AFB3F6DF884B6CEE2E9C7DE9F2AD29000D39200D59500D69700D89800DC - A214EDCE82FCF6E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBCE8BD39200D190 - 00D48E00D58C00D28B00CF8A00CC8900E9C483FFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFCFDFC7891FA406BF04D77F774B5A29BD790E9E2BCDA9107D3 - 9300D59500D69700D89800D99A00DB9C00E1AC28F0D48DFCF5E3FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFEFD599D59605D39200D5940AE3CB86E0CE91DEBE6FDCAE50EFCB94FFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD9E2F4456AF3406BEF5D85F07D - BD9350C052CFE4BEF2D39DD69202D59500D69700D89800D99A00DB9C00DC9D00 - DD9F00E3AA18EFD07EFEFDF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFEECCF87D79806D49400D49100DFA739E0EACE92D394A9 - D599BDDAA5D2E2B9D2EAC9DFF2DDEEF8EBF6FBF3FFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA4 - B4FA4068EC406BF0648BED82C29358BC4B5DC26BEAEFDAF4D8A7DA990CD69700 - D89800D99A00DB9C00DC9D00DD9F00E7B73DF6E4B5FFFFFEFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF7EAE7C063D79800D69600D49400D7 - 9100E8CF90B2D9A54FBC5A51BA5150B94E4DB94852B7415BB84166BC4896D07B - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFF7B93F24068ED406BEF6389F18BC59F58BC4A56BA58 - 6AC97EF0F4E5F8E6C4E1A82ED89800D99A00DB9C00E0A71BF0D691FEFDF9FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E5BCE1AF36D8 - 9800D79800D69600D49400DE9814EFECD276CB8157B95456B84E56B74A56B644 - 55B63F55B53A55B432B4DEA1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF5F7CEF4068ED406CED - 557DF79BC8BB58BD4857BA5857BB5D65C77EE6EDD5FCF8EFF0C36EDFA114E9C1 - 60FAF2DCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 - EFD6EAC465DB9F08D99A00D89800D79800D59600D79504F0CD8FCFE5C251BC5D - 57B95456B84E56B74A56B64455B63F55B5397AC45EF8FCF6FFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFF5372EE4068ED406DED426EF8A5C7D86BC55D57BA5858BB5C59BC625FC4 - 76C6E5C4FFFFFFFBF5E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFF9EED0ECC96FE0A717DC9D00DA9C00D99A00D89800D89A05E3B44B - F6DFB6F8F9F26EC87A57BA5957B95456B84E56B74A56B64455B63F5CB842D3EC - CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFF5776EE4068ED406CED416FF28AABEDA0D4 - A658BC5158BB5D59BC625BBE6759C06F8ED9AAEFF8EEFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFDFAF0F6E2AFEAC156E1A710DEA000DD9E00DC9D00DA9C00 - DDA419E8C36BF7E8C6FFFFFFFFFFFF96DAA658BB5D57BA5857B95456B84E56B7 - 4A56B64455B63FB8E1ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF6B86F04068 - ED406CED4170EE537DF9CBE1E46EC76658BB5C59BC625BBE675CBF6C5EC0706F - CB8ABAE6CAF7FCF9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFEFAF2F8E8BEF0D17EE8B633E1A400E0A200DFA100 - DEA000DFA30EE6B948EFD490FAF1DCFFFFFFFFFFFFFFFFFFBBE7C659BD6158BB - 5C57BB5558BB4E56B94A56B84956B643A2D990FFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFF8CA1F44068ED406CED4170ED4273F298B6F2C7E7CB5BBF5759 - BC635BBE675CBF6C5EC0705FC17461C27985D19AC6E9D0F5FBF7FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFEFDFCF5E3F8E9C2F3D893EDC55DE6B023E2A400E2A400 - E2A400E2A607E5B22BEAC25AF2D999F9EFD4FEFDFAFFFFFFFFFFFFFFFFFFFFFF - FFC5EAD062C06D59BC605DBE60A1D7A7A5D7AC92D09084CB77AEDE9DFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFBECAF94068ED406CED4170ED42 - 75EE4678F7C6DCE9B9E4B45ABD5E5ABE685CBF6C5EC0705FC17461C27963C47E - 65C5827BCD95B7E4C6FCFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFCF7E8FAF0D5FBF2DAFBF2DBFBF2DAFAF0D6FA - EED0F9EBC8F7E6BAF5DFA6F2D68DEFCC6FEBC04EE7B228E4AB15E3A605E2A400 - E3A708E6AF1FEABD47EFCC71F4DDA0F9EECFFDF8EDFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFB9E5C461C16F5ABD6559BC5F7BCB77D4E6EE95B6F4A5 - C3EAB2CDE7CADCEDD8E1FBE6EBFEF0F3FEF7F8FEFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF0 - F3FE587BEF406CED4170ED4275EE4378EE5785FDE5F0F4BEE7BB5EC0665CBF6D - 5EC0705FC17461C27963C47E65C58286D19ECFEDD9FEFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF0D4F5DEA3F3 - DA96F1D487F0CE77EEC969ECC55CEBC04EEABE48EABE4AEBC04EECC255EDC55D - EFCC70F2D589F5DFA5F8E8BEFAF0D6FDFAF1FFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FCF9A2DBB05CBF6D5BBE695ABD655A - BD5CB1DEB7A5C4EE447AFA4378F64274F7416FF8426FF14F78ED5F80F08CA1F4 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFF9BB0F6406CED4170ED4275EE4378EE447CF0 - 6C97FBF1F7F9CDEDCD71C9785EC0705FC17461C2796FC888B2E2C1F7FCF8FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFCFEFDF8FEFCF6FEFCF6FEFCF7FEFDF9 - FFFEFBFFFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFF3E48CD39B5E - BF715CBF6D5BBE695ABD6662C35FDDEEE7749FF9457FEF447BEF4377EE4273EE - 416FED406CED4068EDA1B3F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDF1FD5D82F0 - 4170ED4275EE4378EE447CEF467FF06795FDE0EEF3F1F9F39CDAA269C57C96D6 - A5E5F5E9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED - F8F1AADFB86BC6805EC1735EBF705CBF6D5BBE695BBD64B1E0B2C9DFEC477FF6 - 457FEF447BEF4377EE4273EE416FED406CED6283F0F5F7FEFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFC2D1F94472ED4275EE4378EE447CEF467FEF4784F05A90 - F9C4DAFAFFFFFFE7F7E8FCFEFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFE9F7EDB0E1C078CC9062C37C61C2785EC1735EBF705CBF6E84CF8C - C9EBC6F5FAF96B9AFC4681F0457FEF447BEF4377EE4273EE416FED4671EEC8D4 - FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9BB4F64275EE4378 - EE447CEF467FEF4784EF4A87F04C8AF599BFF6F5F9FDFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFCFEFDDCF2E4A7DEB975CB9065C58364C48062C37C61C278 - 67C57B94D5A0CFECD3FAFDFAFFFFFF9EC0F54884F24682EF457FEF447BEF4377 - EE4273EE416FEDA2B8F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFAFBFF87A7F44378EE447CEF467FEF4784EF4A87F04B8BF04E8FF36C - A2F8C2DAFDFCFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFBFDFCE4F5E9BEE7CC93D6AA6FC98D6AC78867C685 - 65C58368C68384D098AFE0BBDFF3E3FFFFFFFFFFFFFFFFFFC3D7FF4E8AF44885 - EF4682EF457FEF447BEF4377EE4273EE8AA6F4FBFCFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FAFE88AAF4447CEF467FEF47 - 84EF4A87F04B8BF14E90F14F92F05297F282B5F5CAE0FBFAFCFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFF6FCF8E2F4E8CAEBD6ABDFBD89D3A26DC88C6DC88C - 6DC88C6CC88B7DCE9795D7AAB9E5C7DDF2E3FBFDFBFFFFFFFFFFFFFFFFFFFFFF - FFCCDFFD5895F14A88F04885F04682EF457FEF447BEF4377EE8CAAF5F8FAFEFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFBFCFF9DBAF74881EF4784EF4A87F04B8BF14E90F14F92F05297F2549AF2 - 579DF37AB4F5B8D9FAFCFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFF4FBF6ECF8F0EEF9F1EEF9F1EDF8F1ECF8F0EA - F7EFE8F6EDE2F4E8D8F0E0CAEBD6B9E5C8A7DEBA8FD5A77BCD9771CA8F6DC88C - 6DC88C78CC958AD3A3A2DCB6BEE6CCDAF1E1EFF9F2FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFC2D8FC5997F24B8CF04A88F04885F04682EF457FEF48 - 7EEF9EB9F6FBFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBCD1F95B92F14A87F04B8BF1 - 4E90F14F92F05297F2549AF2579DF36EAEF5C1DDFAFCFEFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4F5EAC6E9D2BE - E6CCB5E3C4ACE0BEA5DDB89EDAB296D8AC90D5A88CD4A590D5A895D7AC9BD9B0 - A3DCB6AFE1C0C0E7CDD1EEDBDFF3E6EEF9F1FDFEFEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFDFFAACBFA5595F04E90F14B8CF04A - 88F04885F04682EF588CF1C1D4FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFDFEAFC85AEF54B8BF14E90F14F92F05297F25C9FF3A3C9F8EFF6FEFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFCFEFCF9FDFAF8FCF9F6FCF8F6FCF8F6FCF8F8FCFA - FAFDFBFCFEFCFEFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F0FD8CBBF652 - 96F25092F04E90F14B8CF04A88F04885F080A9F4DCE7FCFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFFC2D8FA73A7F45495F081B3 - F6D6E7FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 - F8FEB0D3F966A5F4549AF25296F25092F04E90F14B8CF06FA0F3C1D5FAFCFDFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFF3F8FECFE2FBF8FBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFF0F7FEB8D8F979B4F558A1F3569CF3549AF25296F25092F071A6F4 - B5D0F9F4F8FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFE2EFFDA9D1F972B3F55DA7F25BA4F358A1F3569CF3 - 579CF281B2F6C1D8FAF6F9FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFCFDFFE9F3FDC0DDFA90C3F767ADF45DA8F35DA8F3 - 5DA7F25EA6F372B0F5A0C7F8D5E6FCFDFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFEFEFFE9F3FDCFE5FBAED4F988BFF663ABF35DA8F3 - 5DA8F35DA8F368AEF485BDF6A9D0F8CFE5FCF5F9FEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFF4F9FEEDF5FEEEF6FEF0F7FEF1F7FEF0F7FEEE - F6FEEAF4FDE5F1FDDDEDFDCFE5FBBDDCFAA8D0F990C3F775B5F563ABF35DA8F3 - 5DA8F365ACF476B5F58DC2F7ACD3F9CCE3FBE9F3FDFAFCFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFEEFDBDDCFAB5 - D7FAACD2F9A2CDF898C8F78FC3F786BEF680BBF67BB8F577B6F57CB9F586BEF6 - 92C4F79ECBF8AED4F9C3DFFBD6E9FCE5F1FDF6FAFEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000} - end - object Label1: TLabel - Left = 127 - Top = 24 - Width = 138 - Height = 16 - Alignment = taCenter - AutoSize = False - Caption = 'TPSQLUpdateSQL' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlack - Font.Height = -12 - Font.Name = 'Arial' - Font.Style = [fsBold] - ParentFont = False - Transparent = True - end - object Label2: TLabel - Left = 13 - Top = 173 - Width = 213 - Height = 13 - Caption = '(c) Pavlo Golub' - end - object VersionLabel: TLabel - Left = 130 - Top = 50 - Width = 135 - Height = 13 - Alignment = taCenter - AutoSize = False - Caption = 'V.' - Transparent = True - end - object Label5: TLabel - Left = 16 - Top = 96 - Width = 257 - Height = 41 - Alignment = taCenter - AutoSize = False - Caption = 'Direct Access Components for PostgreSQL(tm)' - Font.Charset = ANSI_CHARSET - Font.Color = clBlack - Font.Height = -16 - Font.Name = 'Arial' - Font.Style = [fsBold] - ParentFont = False - WordWrap = True - end - object RegLabel: TLabel - Left = 13 - Top = 152 - Width = 260 - Height = 13 - Alignment = taCenter - AutoSize = False - Font.Charset = DEFAULT_CHARSET - Font.Color = clMaroon - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [fsBold] - ParentFont = False - end - object Label3: TLabel - Left = 16 - Top = 192 - Width = 71 - Height = 13 - Cursor = crHandPoint - Caption = 'Ask for support' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - ParentFont = False - OnClick = SpeedButton1Click - end - object Label4: TLabel - Left = 16 - Top = 208 - Width = 72 - Height = 13 - Cursor = crHandPoint - Caption = 'Check updates' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - ParentFont = False - OnClick = SpeedButton2Click - end - object Label6: TLabel - Left = 16 - Top = 224 - Width = 43 - Height = 13 - Cursor = crHandPoint - Caption = 'Buy Now' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - ParentFont = False - OnClick = SpeedButton3Click - end - object Button1: TButton - Left = 207 - Top = 248 - Width = 75 - Height = 25 - Anchors = [akRight] - Caption = 'OK' - ModalResult = 1 - TabOrder = 0 - end -end +object PSQLAboutComp: TPSQLAboutComp + Left = 411 + Top = 251 + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'About...' + ClientHeight = 278 + ClientWidth = 287 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + OldCreateOrder = True + Position = poScreenCenter + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object Bevel1: TBevel + Left = 8 + Top = 8 + Width = 273 + Height = 233 + Shape = bsFrame + end + object Image1: TImage + Left = 13 + Top = 14 + Width = 260 + Height = 59 + AutoSize = True + Center = True + Picture.Data = { + 07544269746D6170FAB30000424DFAB300000000000036000000280000000401 + 00003B0000000100180000000000C4B30000120B0000120B0000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFBF5EFD59BF2DFB1FFFFFEFF + FFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFF + BBBBBBBBBBBBFFFFFFEEEEEEBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFEEEEEE8888 + 88888888888888EEEEEEFFFFFFFFFFFFCCCCCCBBBBBBEEEEEEFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFF999999888888888888DDDDDDFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFEEEEEE999999888888888888999999EEEEEEFFFFFFFFFFFFFFFF + FFFFFFFFDDDDDDBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBDD + DDDDBBBBBBBBBBBBDDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCBBBBBB + BBBBBBEEEEEEFFFFFFBBBBBBBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7EDD5E3 + BA5EEED396FDFBF4FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000 + 000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFF + FFFFFFFFCCCCCC111111000000000000000000111111CCCCCCFFFFFF44444400 + 0000BBBBBBFFFFFFFFFFFFFFFFFFEEEEEE333333000000000000000000000000 + 999999FFFFFFFFFFFFFFFFFFFFFFFF9999991111110000000000000000000000 + 00111111999999FFFFFFFFFFFFFFFFFF88888800000000000000000000000000 + 0000000000000000000000999999000000000000666666FFFFFFFFFFFFFFFFFF + FFFFFFFFFFFF222222000000000000DDDDDDFFFFFF000000000000444444FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFF1DDB1D9A52EF0DBAAFFFFFFFFFFFFFFFFFFFFFFFF000000000000 + FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFF + FFBBBBBB000000444444FFFFFFFFFFFF33333300000022222288888811111100 + 0000444444FFFFFF444444000000BBBBBBFFFFFFFFFFFFFFFFFF777777000000 + 000000777777333333000000111111DDDDDDFFFFFFFFFFFFBBBBBB0000000000 + 00000000000000000000000000000000000000BBBBBBFFFFFFFFFFFF88888800 + 0000000000000000000000000000000000000000000000EEEEEE000000000000 + 111111FFFFFFFFFFFFFFFFFFFFFFFFCCCCCC000000000000333333FFFFFFFFFF + FF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFECD29BD2940CEFDAA9FFFFFFFFFFFFFFFFFF + FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFF + FFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFFCCCCCC00000011 + 1111DDDDDDFFFFFFDDDDDD000000000000DDDDDD444444000000BBBBBBFFFFFF + FFFFFFFFFFFF111111000000888888FFFFFFEEEEEE222222000000888888FFFF + FFFFFFFF22222200000000000033333399999999999933333300000000000022 + 2222FFFFFFFFFFFF888888000000000000666666888888888888888888888888 + 888888FFFFFF555555000000000000CCCCCCFFFFFFFFFFFFFFFFFF8888880000 + 00000000777777FFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDD5A1CF8D00E7C57C + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFF + FF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB00000044 + 4444FFFFFF888888000000555555FFFFFFFFFFFFFFFFFF777777666666DDDDDD + 444444000000BBBBBBFFFFFFFFFFFFCCCCCC000000111111FFFFFFFFFFFFFFFF + FF888888000000555555FFFFFFAAAAAA000000000000222222EEEEEEFFFFFFFF + FFFFEEEEEE222222000000000000AAAAAAFFFFFF888888000000000000BBBBBB + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8888880000000000000000000000 + 00000000000000000000000000000000CCCCCCFFFFFFFFFFFF00000000000044 + 4444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + F0DCB2CE8F07DAA93CFEFCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000 + 00000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF00000000 + 0000FFFFFFBBBBBB000000444444FFFFFF888888000000888888FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB0000 + 00444444FFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFF88888800000000 + 0000999999FFFFFFFFFFFFFFFFFFFFFFFF999999000000000000888888FFFFFF + 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDD + DD000000000000000000000000000000000000000000000000111111FFFFFFFF + FFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFF7EDD8D1961ACE8C01F3E2BEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FF + FFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFF888888 + 000000888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBB + BBFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFFFFFFFFBBBBBB00000044 + 4444FFFFFF444444000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD + 000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFF33333300000000000033333344444422222200 + 0000000000666666FFFFFFFFFFFFFFFFFF000000000000111111444444444444 + 444444777777DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFEFCF8DAAB4CCC8A00DBAB44FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFAFDFAFFFFFFFFFFFF000000000000EEEEEEFF + FFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB + 000000444444FFFFFF888888000000666666FFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB000000222222FFFFFFFF + FFFFFFFFFF999999000000444444FFFFFF444444000000000000FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFF8888880000000000 + 00BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800000000 + 0000999999FFFFFF666666000000000000999999FFFFFFFFFFFFFFFFFF000000 + 000000000000000000000000000000000000111111CCCCCCFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDD7 + AACB8800CC8A00EACE93FFFFFFFFFFFFFFFFFFFDFEFDC4E8C9CCEBD1FEFEFEFF + FFFF000000000000999999FFFFFFDDDDDD000000000000AAAAAAFFFFFFEEEEEE + 000000000000FFFFFFBBBBBB000000444444FFFFFFBBBBBB000000222222EEEE + EEFFFFFFDDDDDD000000333333FFFFFF444444000000777777FFFFFFFFFFFFFF + FFFF000000000000BBBBBBFFFFFFFFFFFF444444000000888888FFFFFF444444 + 000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000004444 + 44FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFCCCCCC000000000000555555FFFFFF111111000000000000EEEEEE + FFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000 + 00222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFEFDFAD5A33BCB8800CD8D07F5E6C9FFFFFFFFFFFFEBF7EC9B + D7A1B6E2BCF5FBF6FFFFFFFFFFFF000000000000111111999999666666000000 + 000000111111AAAAAA555555000000222222FFFFFFBBBBBB000000444444FFFF + FFFFFFFF111111000000555555BBBBBB333333000000555555FFFFFF44444400 + 0000111111999999888888FFFFFF555555000000111111AAAAAA666666000000 + 000000CCCCCCFFFFFF444444000000000000EEEEEEFFFFFFFFFFFFFFFFFFFFFF + FFEEEEEE000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF222222000000111111CCCCCC + 000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFF + FFFFFFFFBBBBBB111111000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF1E1C0CA8904CB8800D4951AF8 + F1E0FFFFFFD4EED67AC97DBDE4C0FFFFFFFFFFFFFFFFFFFFFFFF000000000000 + 111111000000000000000000555555000000000000000000000000777777FFFF + FFBBBBBB000000444444FFFFFFFFFFFF99999900000000000000000000000000 + 0000BBBBBBFFFFFF444444000000000000000000000000DDDDDDDDDDDD111111 + 000000000000000000000000777777FFFFFFFFFFFF777777000000000000AAAA + AAFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000777777FFFFFF88888800 + 0000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 777777000000000000444444000000000000888888FFFFFFFFFFFFFFFFFFFFFF + FF000000000000444444FFFFFFFFFFFFFFFFFF777777000000000000BBBBBBFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFE3 + C07CC98700CA8800DC9723FBFBF6C6EACB62BE62BEE4BFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFF888888888888CCCCCC555555444444888888FFFFFFAAAAAA4444 + 44444444888888FFFFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFBB + BBBB555555444444555555BBBBBBFFFFFFFFFFFF999999888888888888444444 + 666666EEEEEEFFFFFFDDDDDD666666444444444444999999FFFFFFFFFFFFFFFF + FF999999000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF44444400000000 + 0000999999FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000000000000000000000000000CCCC + CCFFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFF66 + 6666000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFD8A84AC88700D08703F0D4A4D3E8C752BD59A2D9A3 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEE111111000000000000666666DDDDDDEE + EEEE666666000000000000111111EEEEEEFFFFFF888888000000000000BBBBBB + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1111110000 + 00000000000000222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000033 + 3333BBBBBBBBBBBB888888000000000000000000DDDDDDFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD09827CB8600E5C685 + E6EDD55BBF5D77C573F8FCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800 + 0000000000000000000000000000000000000000000000888888FFFFFFFFFFFF + 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF666666000000000000000000666666FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFF000000000000000000000000000000000000000000000000444444 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFCB8C0FE0A03EF1F2E269C66A54B94ECDECCEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF666666000000000000000000000000000000000000 + 777777FFFFFFFFFFFFFFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000000000AA + AAAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000 + 000000000000111111CCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFD69424EBE3C39BD18851B84884C877FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFBFDFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD + 888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB666666 + 444444444444666666BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB8888888888 + 88DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFF888888888888888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF888888 + 888888888888888888888888888888999999EEEEEEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFEBD09ED3E5 + BF4FBB484FBA4CABC16CF9E9CFFFFFFFFFFFFFFFFFFFC7DAFAC9DCFBFEFEFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFEFDFB7FCA7053B7415DBE57B7AF4AEBAC47FFFFFFF0F5FF9A + BBF7A8C6F8F1F6FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFCFEFCF9FFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFDEECCF54B73E53B7416ABF5ECD + B048ECCE99E5E7ED6C9FFFA9C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0DCAFDAA83ADE + B152E3BE70E8C98BEDD5A5F1E0BEF7ECD8FBF5EBFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA9 + DB9A56B53953B6426AC252F2EBCFE0E1EA4B8AFFBAC5DBFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFE3BD68CF8E00CE8C00CD8B00CC8900CB8800CB8803CA8909DAAE58 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFF89CB6F55B53955B73DBCE2B1DCE6EE447FFF8B9EC8 + E1B354F8E5C1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF8EFD7A027CF8E00CE8C00CD8B00CC8900 + CB8800CA8700C88500E9CE9AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF70C05055B637A4D996 + E1EBF34A7CFF688FE3CEA95CDA9200D79C17F2DEAFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECD093D08F00 + CF8E00CE8C00CD8B00CC8900CB8800CA8700D6A440FDFBF6FFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFF60B93F79C857E8F3EC658AF63C71FCAFA495DB9409D19100D39200D698 + 08EBCD85FEFCF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFAF4E5D79D1FD08F00CF8E00CE8C00CD8B00CC8900CB8800CD8E0EF3E5 + C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFF6ABF40CFE8CE94AEF03E6DFA7A9FEBD9C0 + 84D88F00D19100D39200D59500D69700E3B548F8EBCDFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3B95CD19000D08F00CF8E00CE8C00CD8B + 00CC8900CA8801EBD2A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFB8E0A6CADC + E5446AFB3F6DF884B6CEE2E9C7DE9F2AD29000D39200D59500D69700D89800DC + A214EDCE82FCF6E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBCE8BD39200D190 + 00D48E00D58C00D28B00CF8A00CC8900E9C483FFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFCFDFC7891FA406BF04D77F774B5A29BD790E9E2BCDA9107D3 + 9300D59500D69700D89800D99A00DB9C00E1AC28F0D48DFCF5E3FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFEFD599D59605D39200D5940AE3CB86E0CE91DEBE6FDCAE50EFCB94FFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD9E2F4456AF3406BEF5D85F07D + BD9350C052CFE4BEF2D39DD69202D59500D69700D89800D99A00DB9C00DC9D00 + DD9F00E3AA18EFD07EFEFDF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFEECCF87D79806D49400D49100DFA739E0EACE92D394A9 + D599BDDAA5D2E2B9D2EAC9DFF2DDEEF8EBF6FBF3FFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA4 + B4FA4068EC406BF0648BED82C29358BC4B5DC26BEAEFDAF4D8A7DA990CD69700 + D89800D99A00DB9C00DC9D00DD9F00E7B73DF6E4B5FFFFFEFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF7EAE7C063D79800D69600D49400D7 + 9100E8CF90B2D9A54FBC5A51BA5150B94E4DB94852B7415BB84166BC4896D07B + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFF7B93F24068ED406BEF6389F18BC59F58BC4A56BA58 + 6AC97EF0F4E5F8E6C4E1A82ED89800D99A00DB9C00E0A71BF0D691FEFDF9FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E5BCE1AF36D8 + 9800D79800D69600D49400DE9814EFECD276CB8157B95456B84E56B74A56B644 + 55B63F55B53A55B432B4DEA1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF5F7CEF4068ED406CED + 557DF79BC8BB58BD4857BA5857BB5D65C77EE6EDD5FCF8EFF0C36EDFA114E9C1 + 60FAF2DCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 + EFD6EAC465DB9F08D99A00D89800D79800D59600D79504F0CD8FCFE5C251BC5D + 57B95456B84E56B74A56B64455B63F55B5397AC45EF8FCF6FFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFF5372EE4068ED406DED426EF8A5C7D86BC55D57BA5858BB5C59BC625FC4 + 76C6E5C4FFFFFFFBF5E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFF9EED0ECC96FE0A717DC9D00DA9C00D99A00D89800D89A05E3B44B + F6DFB6F8F9F26EC87A57BA5957B95456B84E56B74A56B64455B63F5CB842D3EC + CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFF5776EE4068ED406CED416FF28AABEDA0D4 + A658BC5158BB5D59BC625BBE6759C06F8ED9AAEFF8EEFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFDFAF0F6E2AFEAC156E1A710DEA000DD9E00DC9D00DA9C00 + DDA419E8C36BF7E8C6FFFFFFFFFFFF96DAA658BB5D57BA5857B95456B84E56B7 + 4A56B64455B63FB8E1ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF6B86F04068 + ED406CED4170EE537DF9CBE1E46EC76658BB5C59BC625BBE675CBF6C5EC0706F + CB8ABAE6CAF7FCF9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFEFAF2F8E8BEF0D17EE8B633E1A400E0A200DFA100 + DEA000DFA30EE6B948EFD490FAF1DCFFFFFFFFFFFFFFFFFFBBE7C659BD6158BB + 5C57BB5558BB4E56B94A56B84956B643A2D990FFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFF8CA1F44068ED406CED4170ED4273F298B6F2C7E7CB5BBF5759 + BC635BBE675CBF6C5EC0705FC17461C27985D19AC6E9D0F5FBF7FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFEFDFCF5E3F8E9C2F3D893EDC55DE6B023E2A400E2A400 + E2A400E2A607E5B22BEAC25AF2D999F9EFD4FEFDFAFFFFFFFFFFFFFFFFFFFFFF + FFC5EAD062C06D59BC605DBE60A1D7A7A5D7AC92D09084CB77AEDE9DFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFBECAF94068ED406CED4170ED42 + 75EE4678F7C6DCE9B9E4B45ABD5E5ABE685CBF6C5EC0705FC17461C27963C47E + 65C5827BCD95B7E4C6FCFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFCF7E8FAF0D5FBF2DAFBF2DBFBF2DAFAF0D6FA + EED0F9EBC8F7E6BAF5DFA6F2D68DEFCC6FEBC04EE7B228E4AB15E3A605E2A400 + E3A708E6AF1FEABD47EFCC71F4DDA0F9EECFFDF8EDFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFB9E5C461C16F5ABD6559BC5F7BCB77D4E6EE95B6F4A5 + C3EAB2CDE7CADCEDD8E1FBE6EBFEF0F3FEF7F8FEFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF0 + F3FE587BEF406CED4170ED4275EE4378EE5785FDE5F0F4BEE7BB5EC0665CBF6D + 5EC0705FC17461C27963C47E65C58286D19ECFEDD9FEFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF0D4F5DEA3F3 + DA96F1D487F0CE77EEC969ECC55CEBC04EEABE48EABE4AEBC04EECC255EDC55D + EFCC70F2D589F5DFA5F8E8BEFAF0D6FDFAF1FFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FCF9A2DBB05CBF6D5BBE695ABD655A + BD5CB1DEB7A5C4EE447AFA4378F64274F7416FF8426FF14F78ED5F80F08CA1F4 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFF9BB0F6406CED4170ED4275EE4378EE447CF0 + 6C97FBF1F7F9CDEDCD71C9785EC0705FC17461C2796FC888B2E2C1F7FCF8FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFCFEFDF8FEFCF6FEFCF6FEFCF7FEFDF9 + FFFEFBFFFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFF3E48CD39B5E + BF715CBF6D5BBE695ABD6662C35FDDEEE7749FF9457FEF447BEF4377EE4273EE + 416FED406CED4068EDA1B3F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDF1FD5D82F0 + 4170ED4275EE4378EE447CEF467FF06795FDE0EEF3F1F9F39CDAA269C57C96D6 + A5E5F5E9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED + F8F1AADFB86BC6805EC1735EBF705CBF6D5BBE695BBD64B1E0B2C9DFEC477FF6 + 457FEF447BEF4377EE4273EE416FED406CED6283F0F5F7FEFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFC2D1F94472ED4275EE4378EE447CEF467FEF4784F05A90 + F9C4DAFAFFFFFFE7F7E8FCFEFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFE9F7EDB0E1C078CC9062C37C61C2785EC1735EBF705CBF6E84CF8C + C9EBC6F5FAF96B9AFC4681F0457FEF447BEF4377EE4273EE416FED4671EEC8D4 + FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9BB4F64275EE4378 + EE447CEF467FEF4784EF4A87F04C8AF599BFF6F5F9FDFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFCFEFDDCF2E4A7DEB975CB9065C58364C48062C37C61C278 + 67C57B94D5A0CFECD3FAFDFAFFFFFF9EC0F54884F24682EF457FEF447BEF4377 + EE4273EE416FEDA2B8F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFAFBFF87A7F44378EE447CEF467FEF4784EF4A87F04B8BF04E8FF36C + A2F8C2DAFDFCFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFBFDFCE4F5E9BEE7CC93D6AA6FC98D6AC78867C685 + 65C58368C68384D098AFE0BBDFF3E3FFFFFFFFFFFFFFFFFFC3D7FF4E8AF44885 + EF4682EF457FEF447BEF4377EE4273EE8AA6F4FBFCFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FAFE88AAF4447CEF467FEF47 + 84EF4A87F04B8BF14E90F14F92F05297F282B5F5CAE0FBFAFCFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFF6FCF8E2F4E8CAEBD6ABDFBD89D3A26DC88C6DC88C + 6DC88C6CC88B7DCE9795D7AAB9E5C7DDF2E3FBFDFBFFFFFFFFFFFFFFFFFFFFFF + FFCCDFFD5895F14A88F04885F04682EF457FEF447BEF4377EE8CAAF5F8FAFEFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFBFCFF9DBAF74881EF4784EF4A87F04B8BF14E90F14F92F05297F2549AF2 + 579DF37AB4F5B8D9FAFCFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFF4FBF6ECF8F0EEF9F1EEF9F1EDF8F1ECF8F0EA + F7EFE8F6EDE2F4E8D8F0E0CAEBD6B9E5C8A7DEBA8FD5A77BCD9771CA8F6DC88C + 6DC88C78CC958AD3A3A2DCB6BEE6CCDAF1E1EFF9F2FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFC2D8FC5997F24B8CF04A88F04885F04682EF457FEF48 + 7EEF9EB9F6FBFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBCD1F95B92F14A87F04B8BF1 + 4E90F14F92F05297F2549AF2579DF36EAEF5C1DDFAFCFEFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4F5EAC6E9D2BE + E6CCB5E3C4ACE0BEA5DDB89EDAB296D8AC90D5A88CD4A590D5A895D7AC9BD9B0 + A3DCB6AFE1C0C0E7CDD1EEDBDFF3E6EEF9F1FDFEFEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFDFFAACBFA5595F04E90F14B8CF04A + 88F04885F04682EF588CF1C1D4FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFDFEAFC85AEF54B8BF14E90F14F92F05297F25C9FF3A3C9F8EFF6FEFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFCFEFCF9FDFAF8FCF9F6FCF8F6FCF8F6FCF8F8FCFA + FAFDFBFCFEFCFEFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F0FD8CBBF652 + 96F25092F04E90F14B8CF04A88F04885F080A9F4DCE7FCFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFFC2D8FA73A7F45495F081B3 + F6D6E7FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 + F8FEB0D3F966A5F4549AF25296F25092F04E90F14B8CF06FA0F3C1D5FAFCFDFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFF3F8FECFE2FBF8FBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFF0F7FEB8D8F979B4F558A1F3569CF3549AF25296F25092F071A6F4 + B5D0F9F4F8FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFE2EFFDA9D1F972B3F55DA7F25BA4F358A1F3569CF3 + 579CF281B2F6C1D8FAF6F9FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFCFDFFE9F3FDC0DDFA90C3F767ADF45DA8F35DA8F3 + 5DA7F25EA6F372B0F5A0C7F8D5E6FCFDFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFEFEFFE9F3FDCFE5FBAED4F988BFF663ABF35DA8F3 + 5DA8F35DA8F368AEF485BDF6A9D0F8CFE5FCF5F9FEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFF4F9FEEDF5FEEEF6FEF0F7FEF1F7FEF0F7FEEE + F6FEEAF4FDE5F1FDDDEDFDCFE5FBBDDCFAA8D0F990C3F775B5F563ABF35DA8F3 + 5DA8F365ACF476B5F58DC2F7ACD3F9CCE3FBE9F3FDFAFCFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFEEFDBDDCFAB5 + D7FAACD2F9A2CDF898C8F78FC3F786BEF680BBF67BB8F577B6F57CB9F586BEF6 + 92C4F79ECBF8AED4F9C3DFFBD6E9FCE5F1FDF6FAFEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000} + end + object Label1: TLabel + Left = 127 + Top = 24 + Width = 138 + Height = 16 + Alignment = taCenter + AutoSize = False + Caption = 'TPSQLUpdateSQL' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlack + Font.Height = -12 + Font.Name = 'Arial' + Font.Style = [fsBold] + ParentFont = False + Transparent = True + end + object Label2: TLabel + Left = 13 + Top = 173 + Width = 213 + Height = 13 + Caption = '(c) Pavlo Golub' + end + object VersionLabel: TLabel + Left = 130 + Top = 50 + Width = 135 + Height = 13 + Alignment = taCenter + AutoSize = False + Caption = 'V.' + Transparent = True + end + object Label5: TLabel + Left = 16 + Top = 96 + Width = 257 + Height = 41 + Alignment = taCenter + AutoSize = False + Caption = 'Direct Access Components for PostgreSQL(tm)' + Font.Charset = ANSI_CHARSET + Font.Color = clBlack + Font.Height = -16 + Font.Name = 'Arial' + Font.Style = [fsBold] + ParentFont = False + WordWrap = True + end + object RegLabel: TLabel + Left = 13 + Top = 152 + Width = 260 + Height = 13 + Alignment = taCenter + AutoSize = False + Font.Charset = DEFAULT_CHARSET + Font.Color = clMaroon + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [fsBold] + ParentFont = False + end + object Label3: TLabel + Left = 16 + Top = 192 + Width = 71 + Height = 13 + Cursor = crHandPoint + Caption = 'Ask for support' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + ParentFont = False + OnClick = SpeedButton1Click + end + object Label4: TLabel + Left = 16 + Top = 208 + Width = 72 + Height = 13 + Cursor = crHandPoint + Caption = 'Check updates' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + ParentFont = False + OnClick = SpeedButton2Click + end + object Label6: TLabel + Left = 16 + Top = 224 + Width = 43 + Height = 13 + Cursor = crHandPoint + Caption = 'Buy Now' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + ParentFont = False + OnClick = SpeedButton3Click + end + object Button1: TButton + Left = 207 + Top = 248 + Width = 75 + Height = 25 + Anchors = [akRight] + Caption = 'OK' + ModalResult = 1 + TabOrder = 0 + end +end diff --git a/psqlAboutFrm.lfm b/Source/psqlAboutFrm.lfm similarity index 98% rename from psqlAboutFrm.lfm rename to Source/psqlAboutFrm.lfm index 7437499..20209cd 100644 --- a/psqlAboutFrm.lfm +++ b/Source/psqlAboutFrm.lfm @@ -1,1593 +1,1593 @@ -object PSQLAboutComp: TPSQLAboutComp - Left = 411 - Top = 251 - BorderIcons = [biSystemMenu] - BorderStyle = bsDialog - Caption = 'About...' - ClientHeight = 278 - ClientWidth = 287 - Color = clBtnFace - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - Position = poScreenCenter - OnCreate = FormCreate - PixelsPerInch = 96 - object Bevel1: TBevel - Left = 8 - Top = 8 - Width = 273 - Height = 233 - Shape = bsFrame - end - object Image1: TImage - Left = 13 - Top = 14 - Width = 260 - Height = 59 - AutoSize = True - Center = True - Picture.Data = { - 07544269746D6170FAB30000424DFAB300000000000036000000280000000401 - 00003B0000000100180000000000C4B30000120B0000120B0000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFBF5EFD59BF2DFB1FFFFFEFF - FFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFF - BBBBBBBBBBBBFFFFFFEEEEEEBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFEEEEEE8888 - 88888888888888EEEEEEFFFFFFFFFFFFCCCCCCBBBBBBEEEEEEFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFF999999888888888888DDDDDDFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFEEEEEE999999888888888888999999EEEEEEFFFFFFFFFFFFFFFF - FFFFFFFFDDDDDDBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBDD - DDDDBBBBBBBBBBBBDDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCBBBBBB - BBBBBBEEEEEEFFFFFFBBBBBBBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7EDD5E3 - BA5EEED396FDFBF4FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000 - 000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFF - FFFFFFFFCCCCCC111111000000000000000000111111CCCCCCFFFFFF44444400 - 0000BBBBBBFFFFFFFFFFFFFFFFFFEEEEEE333333000000000000000000000000 - 999999FFFFFFFFFFFFFFFFFFFFFFFF9999991111110000000000000000000000 - 00111111999999FFFFFFFFFFFFFFFFFF88888800000000000000000000000000 - 0000000000000000000000999999000000000000666666FFFFFFFFFFFFFFFFFF - FFFFFFFFFFFF222222000000000000DDDDDDFFFFFF000000000000444444FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFF1DDB1D9A52EF0DBAAFFFFFFFFFFFFFFFFFFFFFFFF000000000000 - FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFF - FFBBBBBB000000444444FFFFFFFFFFFF33333300000022222288888811111100 - 0000444444FFFFFF444444000000BBBBBBFFFFFFFFFFFFFFFFFF777777000000 - 000000777777333333000000111111DDDDDDFFFFFFFFFFFFBBBBBB0000000000 - 00000000000000000000000000000000000000BBBBBBFFFFFFFFFFFF88888800 - 0000000000000000000000000000000000000000000000EEEEEE000000000000 - 111111FFFFFFFFFFFFFFFFFFFFFFFFCCCCCC000000000000333333FFFFFFFFFF - FF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFECD29BD2940CEFDAA9FFFFFFFFFFFFFFFFFF - FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFF - FFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFFCCCCCC00000011 - 1111DDDDDDFFFFFFDDDDDD000000000000DDDDDD444444000000BBBBBBFFFFFF - FFFFFFFFFFFF111111000000888888FFFFFFEEEEEE222222000000888888FFFF - FFFFFFFF22222200000000000033333399999999999933333300000000000022 - 2222FFFFFFFFFFFF888888000000000000666666888888888888888888888888 - 888888FFFFFF555555000000000000CCCCCCFFFFFFFFFFFFFFFFFF8888880000 - 00000000777777FFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDD5A1CF8D00E7C57C - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFF - FF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB00000044 - 4444FFFFFF888888000000555555FFFFFFFFFFFFFFFFFF777777666666DDDDDD - 444444000000BBBBBBFFFFFFFFFFFFCCCCCC000000111111FFFFFFFFFFFFFFFF - FF888888000000555555FFFFFFAAAAAA000000000000222222EEEEEEFFFFFFFF - FFFFEEEEEE222222000000000000AAAAAAFFFFFF888888000000000000BBBBBB - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8888880000000000000000000000 - 00000000000000000000000000000000CCCCCCFFFFFFFFFFFF00000000000044 - 4444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - F0DCB2CE8F07DAA93CFEFCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000 - 00000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF00000000 - 0000FFFFFFBBBBBB000000444444FFFFFF888888000000888888FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB0000 - 00444444FFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFF88888800000000 - 0000999999FFFFFFFFFFFFFFFFFFFFFFFF999999000000000000888888FFFFFF - 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDD - DD000000000000000000000000000000000000000000000000111111FFFFFFFF - FFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFF7EDD8D1961ACE8C01F3E2BEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FF - FFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFF888888 - 000000888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBB - BBFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFFFFFFFFBBBBBB00000044 - 4444FFFFFF444444000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD - 000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFF33333300000000000033333344444422222200 - 0000000000666666FFFFFFFFFFFFFFFFFF000000000000111111444444444444 - 444444777777DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFEFCF8DAAB4CCC8A00DBAB44FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFAFDFAFFFFFFFFFFFF000000000000EEEEEEFF - FFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB - 000000444444FFFFFF888888000000666666FFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB000000222222FFFFFFFF - FFFFFFFFFF999999000000444444FFFFFF444444000000000000FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFF8888880000000000 - 00BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800000000 - 0000999999FFFFFF666666000000000000999999FFFFFFFFFFFFFFFFFF000000 - 000000000000000000000000000000000000111111CCCCCCFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDD7 - AACB8800CC8A00EACE93FFFFFFFFFFFFFFFFFFFDFEFDC4E8C9CCEBD1FEFEFEFF - FFFF000000000000999999FFFFFFDDDDDD000000000000AAAAAAFFFFFFEEEEEE - 000000000000FFFFFFBBBBBB000000444444FFFFFFBBBBBB000000222222EEEE - EEFFFFFFDDDDDD000000333333FFFFFF444444000000777777FFFFFFFFFFFFFF - FFFF000000000000BBBBBBFFFFFFFFFFFF444444000000888888FFFFFF444444 - 000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000004444 - 44FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFCCCCCC000000000000555555FFFFFF111111000000000000EEEEEE - FFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000 - 00222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFEFDFAD5A33BCB8800CD8D07F5E6C9FFFFFFFFFFFFEBF7EC9B - D7A1B6E2BCF5FBF6FFFFFFFFFFFF000000000000111111999999666666000000 - 000000111111AAAAAA555555000000222222FFFFFFBBBBBB000000444444FFFF - FFFFFFFF111111000000555555BBBBBB333333000000555555FFFFFF44444400 - 0000111111999999888888FFFFFF555555000000111111AAAAAA666666000000 - 000000CCCCCCFFFFFF444444000000000000EEEEEEFFFFFFFFFFFFFFFFFFFFFF - FFEEEEEE000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF222222000000111111CCCCCC - 000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFF - FFFFFFFFBBBBBB111111000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF1E1C0CA8904CB8800D4951AF8 - F1E0FFFFFFD4EED67AC97DBDE4C0FFFFFFFFFFFFFFFFFFFFFFFF000000000000 - 111111000000000000000000555555000000000000000000000000777777FFFF - FFBBBBBB000000444444FFFFFFFFFFFF99999900000000000000000000000000 - 0000BBBBBBFFFFFF444444000000000000000000000000DDDDDDDDDDDD111111 - 000000000000000000000000777777FFFFFFFFFFFF777777000000000000AAAA - AAFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000777777FFFFFF88888800 - 0000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - 777777000000000000444444000000000000888888FFFFFFFFFFFFFFFFFFFFFF - FF000000000000444444FFFFFFFFFFFFFFFFFF777777000000000000BBBBBBFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFE3 - C07CC98700CA8800DC9723FBFBF6C6EACB62BE62BEE4BFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFF888888888888CCCCCC555555444444888888FFFFFFAAAAAA4444 - 44444444888888FFFFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFBB - BBBB555555444444555555BBBBBBFFFFFFFFFFFF999999888888888888444444 - 666666EEEEEEFFFFFFDDDDDD666666444444444444999999FFFFFFFFFFFFFFFF - FF999999000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF44444400000000 - 0000999999FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000000000000000000000000000CCCC - CCFFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFF66 - 6666000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFD8A84AC88700D08703F0D4A4D3E8C752BD59A2D9A3 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEE111111000000000000666666DDDDDDEE - EEEE666666000000000000111111EEEEEEFFFFFF888888000000000000BBBBBB - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1111110000 - 00000000000000222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000033 - 3333BBBBBBBBBBBB888888000000000000000000DDDDDDFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD09827CB8600E5C685 - E6EDD55BBF5D77C573F8FCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800 - 0000000000000000000000000000000000000000000000888888FFFFFFFFFFFF - 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF666666000000000000000000666666FFFFFFFFFFFFFFFFFFFF - FFFFFFFFFF000000000000000000000000000000000000000000000000444444 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFCB8C0FE0A03EF1F2E269C66A54B94ECDECCEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF666666000000000000000000000000000000000000 - 777777FFFFFFFFFFFFFFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000000000AA - AAAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000 - 000000000000111111CCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFD69424EBE3C39BD18851B84884C877FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFBFDFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD - 888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB666666 - 444444444444666666BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB8888888888 - 88DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFF888888888888888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF888888 - 888888888888888888888888888888999999EEEEEEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFEBD09ED3E5 - BF4FBB484FBA4CABC16CF9E9CFFFFFFFFFFFFFFFFFFFC7DAFAC9DCFBFEFEFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFEFDFB7FCA7053B7415DBE57B7AF4AEBAC47FFFFFFF0F5FF9A - BBF7A8C6F8F1F6FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFCFEFCF9FFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFDEECCF54B73E53B7416ABF5ECD - B048ECCE99E5E7ED6C9FFFA9C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0DCAFDAA83ADE - B152E3BE70E8C98BEDD5A5F1E0BEF7ECD8FBF5EBFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA9 - DB9A56B53953B6426AC252F2EBCFE0E1EA4B8AFFBAC5DBFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFE3BD68CF8E00CE8C00CD8B00CC8900CB8800CB8803CA8909DAAE58 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFF89CB6F55B53955B73DBCE2B1DCE6EE447FFF8B9EC8 - E1B354F8E5C1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF8EFD7A027CF8E00CE8C00CD8B00CC8900 - CB8800CA8700C88500E9CE9AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF70C05055B637A4D996 - E1EBF34A7CFF688FE3CEA95CDA9200D79C17F2DEAFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECD093D08F00 - CF8E00CE8C00CD8B00CC8900CB8800CA8700D6A440FDFBF6FFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFF60B93F79C857E8F3EC658AF63C71FCAFA495DB9409D19100D39200D698 - 08EBCD85FEFCF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFAF4E5D79D1FD08F00CF8E00CE8C00CD8B00CC8900CB8800CD8E0EF3E5 - C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFF6ABF40CFE8CE94AEF03E6DFA7A9FEBD9C0 - 84D88F00D19100D39200D59500D69700E3B548F8EBCDFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3B95CD19000D08F00CF8E00CE8C00CD8B - 00CC8900CA8801EBD2A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFB8E0A6CADC - E5446AFB3F6DF884B6CEE2E9C7DE9F2AD29000D39200D59500D69700D89800DC - A214EDCE82FCF6E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBCE8BD39200D190 - 00D48E00D58C00D28B00CF8A00CC8900E9C483FFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFCFDFC7891FA406BF04D77F774B5A29BD790E9E2BCDA9107D3 - 9300D59500D69700D89800D99A00DB9C00E1AC28F0D48DFCF5E3FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFEFD599D59605D39200D5940AE3CB86E0CE91DEBE6FDCAE50EFCB94FFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD9E2F4456AF3406BEF5D85F07D - BD9350C052CFE4BEF2D39DD69202D59500D69700D89800D99A00DB9C00DC9D00 - DD9F00E3AA18EFD07EFEFDF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFEECCF87D79806D49400D49100DFA739E0EACE92D394A9 - D599BDDAA5D2E2B9D2EAC9DFF2DDEEF8EBF6FBF3FFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA4 - B4FA4068EC406BF0648BED82C29358BC4B5DC26BEAEFDAF4D8A7DA990CD69700 - D89800D99A00DB9C00DC9D00DD9F00E7B73DF6E4B5FFFFFEFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF7EAE7C063D79800D69600D49400D7 - 9100E8CF90B2D9A54FBC5A51BA5150B94E4DB94852B7415BB84166BC4896D07B - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFF7B93F24068ED406BEF6389F18BC59F58BC4A56BA58 - 6AC97EF0F4E5F8E6C4E1A82ED89800D99A00DB9C00E0A71BF0D691FEFDF9FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E5BCE1AF36D8 - 9800D79800D69600D49400DE9814EFECD276CB8157B95456B84E56B74A56B644 - 55B63F55B53A55B432B4DEA1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF5F7CEF4068ED406CED - 557DF79BC8BB58BD4857BA5857BB5D65C77EE6EDD5FCF8EFF0C36EDFA114E9C1 - 60FAF2DCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 - EFD6EAC465DB9F08D99A00D89800D79800D59600D79504F0CD8FCFE5C251BC5D - 57B95456B84E56B74A56B64455B63F55B5397AC45EF8FCF6FFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFF5372EE4068ED406DED426EF8A5C7D86BC55D57BA5858BB5C59BC625FC4 - 76C6E5C4FFFFFFFBF5E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFF9EED0ECC96FE0A717DC9D00DA9C00D99A00D89800D89A05E3B44B - F6DFB6F8F9F26EC87A57BA5957B95456B84E56B74A56B64455B63F5CB842D3EC - CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFF5776EE4068ED406CED416FF28AABEDA0D4 - A658BC5158BB5D59BC625BBE6759C06F8ED9AAEFF8EEFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFDFAF0F6E2AFEAC156E1A710DEA000DD9E00DC9D00DA9C00 - DDA419E8C36BF7E8C6FFFFFFFFFFFF96DAA658BB5D57BA5857B95456B84E56B7 - 4A56B64455B63FB8E1ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF6B86F04068 - ED406CED4170EE537DF9CBE1E46EC76658BB5C59BC625BBE675CBF6C5EC0706F - CB8ABAE6CAF7FCF9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFEFAF2F8E8BEF0D17EE8B633E1A400E0A200DFA100 - DEA000DFA30EE6B948EFD490FAF1DCFFFFFFFFFFFFFFFFFFBBE7C659BD6158BB - 5C57BB5558BB4E56B94A56B84956B643A2D990FFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFF8CA1F44068ED406CED4170ED4273F298B6F2C7E7CB5BBF5759 - BC635BBE675CBF6C5EC0705FC17461C27985D19AC6E9D0F5FBF7FFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFEFDFCF5E3F8E9C2F3D893EDC55DE6B023E2A400E2A400 - E2A400E2A607E5B22BEAC25AF2D999F9EFD4FEFDFAFFFFFFFFFFFFFFFFFFFFFF - FFC5EAD062C06D59BC605DBE60A1D7A7A5D7AC92D09084CB77AEDE9DFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFBECAF94068ED406CED4170ED42 - 75EE4678F7C6DCE9B9E4B45ABD5E5ABE685CBF6C5EC0705FC17461C27963C47E - 65C5827BCD95B7E4C6FCFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFCF7E8FAF0D5FBF2DAFBF2DBFBF2DAFAF0D6FA - EED0F9EBC8F7E6BAF5DFA6F2D68DEFCC6FEBC04EE7B228E4AB15E3A605E2A400 - E3A708E6AF1FEABD47EFCC71F4DDA0F9EECFFDF8EDFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFB9E5C461C16F5ABD6559BC5F7BCB77D4E6EE95B6F4A5 - C3EAB2CDE7CADCEDD8E1FBE6EBFEF0F3FEF7F8FEFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF0 - F3FE587BEF406CED4170ED4275EE4378EE5785FDE5F0F4BEE7BB5EC0665CBF6D - 5EC0705FC17461C27963C47E65C58286D19ECFEDD9FEFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF0D4F5DEA3F3 - DA96F1D487F0CE77EEC969ECC55CEBC04EEABE48EABE4AEBC04EECC255EDC55D - EFCC70F2D589F5DFA5F8E8BEFAF0D6FDFAF1FFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FCF9A2DBB05CBF6D5BBE695ABD655A - BD5CB1DEB7A5C4EE447AFA4378F64274F7416FF8426FF14F78ED5F80F08CA1F4 - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFF9BB0F6406CED4170ED4275EE4378EE447CF0 - 6C97FBF1F7F9CDEDCD71C9785EC0705FC17461C2796FC888B2E2C1F7FCF8FFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFCFEFDF8FEFCF6FEFCF6FEFCF7FEFDF9 - FFFEFBFFFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFF3E48CD39B5E - BF715CBF6D5BBE695ABD6662C35FDDEEE7749FF9457FEF447BEF4377EE4273EE - 416FED406CED4068EDA1B3F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDF1FD5D82F0 - 4170ED4275EE4378EE447CEF467FF06795FDE0EEF3F1F9F39CDAA269C57C96D6 - A5E5F5E9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED - F8F1AADFB86BC6805EC1735EBF705CBF6D5BBE695BBD64B1E0B2C9DFEC477FF6 - 457FEF447BEF4377EE4273EE416FED406CED6283F0F5F7FEFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFC2D1F94472ED4275EE4378EE447CEF467FEF4784F05A90 - F9C4DAFAFFFFFFE7F7E8FCFEFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFE9F7EDB0E1C078CC9062C37C61C2785EC1735EBF705CBF6E84CF8C - C9EBC6F5FAF96B9AFC4681F0457FEF447BEF4377EE4273EE416FED4671EEC8D4 - FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9BB4F64275EE4378 - EE447CEF467FEF4784EF4A87F04C8AF599BFF6F5F9FDFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFCFEFDDCF2E4A7DEB975CB9065C58364C48062C37C61C278 - 67C57B94D5A0CFECD3FAFDFAFFFFFF9EC0F54884F24682EF457FEF447BEF4377 - EE4273EE416FEDA2B8F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFAFBFF87A7F44378EE447CEF467FEF4784EF4A87F04B8BF04E8FF36C - A2F8C2DAFDFCFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFBFDFCE4F5E9BEE7CC93D6AA6FC98D6AC78867C685 - 65C58368C68384D098AFE0BBDFF3E3FFFFFFFFFFFFFFFFFFC3D7FF4E8AF44885 - EF4682EF457FEF447BEF4377EE4273EE8AA6F4FBFCFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FAFE88AAF4447CEF467FEF47 - 84EF4A87F04B8BF14E90F14F92F05297F282B5F5CAE0FBFAFCFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFF6FCF8E2F4E8CAEBD6ABDFBD89D3A26DC88C6DC88C - 6DC88C6CC88B7DCE9795D7AAB9E5C7DDF2E3FBFDFBFFFFFFFFFFFFFFFFFFFFFF - FFCCDFFD5895F14A88F04885F04682EF457FEF447BEF4377EE8CAAF5F8FAFEFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFBFCFF9DBAF74881EF4784EF4A87F04B8BF14E90F14F92F05297F2549AF2 - 579DF37AB4F5B8D9FAFCFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFF4FBF6ECF8F0EEF9F1EEF9F1EDF8F1ECF8F0EA - F7EFE8F6EDE2F4E8D8F0E0CAEBD6B9E5C8A7DEBA8FD5A77BCD9771CA8F6DC88C - 6DC88C78CC958AD3A3A2DCB6BEE6CCDAF1E1EFF9F2FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFC2D8FC5997F24B8CF04A88F04885F04682EF457FEF48 - 7EEF9EB9F6FBFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBCD1F95B92F14A87F04B8BF1 - 4E90F14F92F05297F2549AF2579DF36EAEF5C1DDFAFCFEFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4F5EAC6E9D2BE - E6CCB5E3C4ACE0BEA5DDB89EDAB296D8AC90D5A88CD4A590D5A895D7AC9BD9B0 - A3DCB6AFE1C0C0E7CDD1EEDBDFF3E6EEF9F1FDFEFEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFDFFAACBFA5595F04E90F14B8CF04A - 88F04885F04682EF588CF1C1D4FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFDFEAFC85AEF54B8BF14E90F14F92F05297F25C9FF3A3C9F8EFF6FEFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFCFEFCF9FDFAF8FCF9F6FCF8F6FCF8F6FCF8F8FCFA - FAFDFBFCFEFCFEFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F0FD8CBBF652 - 96F25092F04E90F14B8CF04A88F04885F080A9F4DCE7FCFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFFC2D8FA73A7F45495F081B3 - F6D6E7FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 - F8FEB0D3F966A5F4549AF25296F25092F04E90F14B8CF06FA0F3C1D5FAFCFDFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFF3F8FECFE2FBF8FBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFF0F7FEB8D8F979B4F558A1F3569CF3549AF25296F25092F071A6F4 - B5D0F9F4F8FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFE2EFFDA9D1F972B3F55DA7F25BA4F358A1F3569CF3 - 579CF281B2F6C1D8FAF6F9FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFCFDFFE9F3FDC0DDFA90C3F767ADF45DA8F35DA8F3 - 5DA7F25EA6F372B0F5A0C7F8D5E6FCFDFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 - 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFEFEFFE9F3FDCFE5FBAED4F988BFF663ABF35DA8F3 - 5DA8F35DA8F368AEF485BDF6A9D0F8CFE5FCF5F9FEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFF4F9FEEDF5FEEEF6FEF0F7FEF1F7FEF0F7FEEE - F6FEEAF4FDE5F1FDDDEDFDCFE5FBBDDCFAA8D0F990C3F775B5F563ABF35DA8F3 - 5DA8F365ACF476B5F58DC2F7ACD3F9CCE3FBE9F3FDFAFCFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFEEFDBDDCFAB5 - D7FAACD2F9A2CDF898C8F78FC3F786BEF680BBF67BB8F577B6F57CB9F586BEF6 - 92C4F79ECBF8AED4F9C3DFFBD6E9FCE5F1FDF6FAFEFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000} - end - object Label1: TLabel - Left = 127 - Top = 24 - Width = 138 - Height = 16 - Alignment = taCenter - AutoSize = False - Caption = 'TPSQLUpdateSQL' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlack - Font.Height = -12 - Font.Name = 'Arial' - Font.Style = [fsBold] - ParentFont = False - Transparent = True - end - object Label2: TLabel - Left = 13 - Top = 173 - Width = 213 - Height = 13 - Caption = '(c) Pavlo Golub' - end - object VersionLabel: TLabel - Left = 130 - Top = 50 - Width = 135 - Height = 13 - Alignment = taCenter - AutoSize = False - Caption = 'V.' - Transparent = True - end - object Label5: TLabel - Left = 16 - Top = 96 - Width = 257 - Height = 41 - Alignment = taCenter - AutoSize = False - Caption = 'Direct Access Components for PostgreSQL(tm)' - Font.Charset = ANSI_CHARSET - Font.Color = clBlack - Font.Height = -16 - Font.Name = 'Arial' - Font.Style = [fsBold] - ParentFont = False - WordWrap = True - end - object RegLabel: TLabel - Left = 13 - Top = 152 - Width = 260 - Height = 13 - Alignment = taCenter - AutoSize = False - Font.Charset = DEFAULT_CHARSET - Font.Color = clMaroon - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [fsBold] - ParentFont = False - end - object Label3: TLabel - Left = 16 - Top = 192 - Width = 71 - Height = 13 - Cursor = crHandPoint - Caption = 'Ask for support' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - ParentFont = False - OnClick = SpeedButton1Click - end - object Label4: TLabel - Left = 16 - Top = 208 - Width = 72 - Height = 13 - Cursor = crHandPoint - Caption = 'Check updates' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - ParentFont = False - OnClick = SpeedButton2Click - end - object Label6: TLabel - Left = 16 - Top = 224 - Width = 43 - Height = 13 - Cursor = crHandPoint - Caption = 'Buy Now' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'MS Sans Serif' - Font.Style = [] - ParentFont = False - OnClick = SpeedButton3Click - end - object Button1: TButton - Left = 207 - Top = 248 - Width = 75 - Height = 25 - Anchors = [akRight] - Caption = 'OK' - ModalResult = 1 - TabOrder = 0 - end -end +object PSQLAboutComp: TPSQLAboutComp + Left = 411 + Top = 251 + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'About...' + ClientHeight = 278 + ClientWidth = 287 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + Position = poScreenCenter + OnCreate = FormCreate + PixelsPerInch = 96 + object Bevel1: TBevel + Left = 8 + Top = 8 + Width = 273 + Height = 233 + Shape = bsFrame + end + object Image1: TImage + Left = 13 + Top = 14 + Width = 260 + Height = 59 + AutoSize = True + Center = True + Picture.Data = { + 07544269746D6170FAB30000424DFAB300000000000036000000280000000401 + 00003B0000000100180000000000C4B30000120B0000120B0000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFBF5EFD59BF2DFB1FFFFFEFF + FFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFFBBBBBBBBBBBBFFFFFFFFFFFFFFFFFF + BBBBBBBBBBBBFFFFFFEEEEEEBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFEEEEEE8888 + 88888888888888EEEEEEFFFFFFFFFFFFCCCCCCBBBBBBEEEEEEFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFF999999888888888888DDDDDDFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFEEEEEE999999888888888888999999EEEEEEFFFFFFFFFFFFFFFF + FFFFFFFFDDDDDDBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBDD + DDDDBBBBBBBBBBBBDDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCBBBBBB + BBBBBBEEEEEEFFFFFFBBBBBBBBBBBBCCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7EDD5E3 + BA5EEED396FDFBF4FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000 + 000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFF + FFFFFFFFCCCCCC111111000000000000000000111111CCCCCCFFFFFF44444400 + 0000BBBBBBFFFFFFFFFFFFFFFFFFEEEEEE333333000000000000000000000000 + 999999FFFFFFFFFFFFFFFFFFFFFFFF9999991111110000000000000000000000 + 00111111999999FFFFFFFFFFFFFFFFFF88888800000000000000000000000000 + 0000000000000000000000999999000000000000666666FFFFFFFFFFFFFFFFFF + FFFFFFFFFFFF222222000000000000DDDDDDFFFFFF000000000000444444FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFF1DDB1D9A52EF0DBAAFFFFFFFFFFFFFFFFFFFFFFFF000000000000 + FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFF + FFBBBBBB000000444444FFFFFFFFFFFF33333300000022222288888811111100 + 0000444444FFFFFF444444000000BBBBBBFFFFFFFFFFFFFFFFFF777777000000 + 000000777777333333000000111111DDDDDDFFFFFFFFFFFFBBBBBB0000000000 + 00000000000000000000000000000000000000BBBBBBFFFFFFFFFFFF88888800 + 0000000000000000000000000000000000000000000000EEEEEE000000000000 + 111111FFFFFFFFFFFFFFFFFFFFFFFFCCCCCC000000000000333333FFFFFFFFFF + FF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFECD29BD2940CEFDAA9FFFFFFFFFFFFFFFFFF + FFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFF + FFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFFCCCCCC00000011 + 1111DDDDDDFFFFFFDDDDDD000000000000DDDDDD444444000000BBBBBBFFFFFF + FFFFFFFFFFFF111111000000888888FFFFFFEEEEEE222222000000888888FFFF + FFFFFFFF22222200000000000033333399999999999933333300000000000022 + 2222FFFFFFFFFFFF888888000000000000666666888888888888888888888888 + 888888FFFFFF555555000000000000CCCCCCFFFFFFFFFFFFFFFFFF8888880000 + 00000000777777FFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEDD5A1CF8D00E7C57C + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFF + FF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB00000044 + 4444FFFFFF888888000000555555FFFFFFFFFFFFFFFFFF777777666666DDDDDD + 444444000000BBBBBBFFFFFFFFFFFFCCCCCC000000111111FFFFFFFFFFFFFFFF + FF888888000000555555FFFFFFAAAAAA000000000000222222EEEEEEFFFFFFFF + FFFFEEEEEE222222000000000000AAAAAAFFFFFF888888000000000000BBBBBB + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8888880000000000000000000000 + 00000000000000000000000000000000CCCCCCFFFFFFFFFFFF00000000000044 + 4444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + F0DCB2CE8F07DAA93CFEFCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000 + 00000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF00000000 + 0000FFFFFFBBBBBB000000444444FFFFFF888888000000888888FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB0000 + 00444444FFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFF88888800000000 + 0000999999FFFFFFFFFFFFFFFFFFFFFFFF999999000000000000888888FFFFFF + 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDD + DD000000000000000000000000000000000000000000000000111111FFFFFFFF + FFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFF7EDD8D1961ACE8C01F3E2BEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FF + FFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB000000444444FFFFFF888888 + 000000888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF444444000000BBBB + BBFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFFFFFFFFBBBBBB00000044 + 4444FFFFFF444444000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD + 000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFF33333300000000000033333344444422222200 + 0000000000666666FFFFFFFFFFFFFFFFFF000000000000111111444444444444 + 444444777777DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFEFCF8DAAB4CCC8A00DBAB44FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFAFDFAFFFFFFFFFFFF000000000000EEEEEEFF + FFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFF000000000000FFFFFFBBBBBB + 000000444444FFFFFF888888000000666666FFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFF444444000000BBBBBBFFFFFFFFFFFFBBBBBB000000222222FFFFFFFF + FFFFFFFFFF999999000000444444FFFFFF444444000000000000FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFF8888880000000000 + 00BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800000000 + 0000999999FFFFFF666666000000000000999999FFFFFFFFFFFFFFFFFF000000 + 000000000000000000000000000000000000111111CCCCCCFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDD7 + AACB8800CC8A00EACE93FFFFFFFFFFFFFFFFFFFDFEFDC4E8C9CCEBD1FEFEFEFF + FFFF000000000000999999FFFFFFDDDDDD000000000000AAAAAAFFFFFFEEEEEE + 000000000000FFFFFFBBBBBB000000444444FFFFFFBBBBBB000000222222EEEE + EEFFFFFFDDDDDD000000333333FFFFFF444444000000777777FFFFFFFFFFFFFF + FFFF000000000000BBBBBBFFFFFFFFFFFF444444000000888888FFFFFF444444 + 000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000004444 + 44FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFCCCCCC000000000000555555FFFFFF111111000000000000EEEEEE + FFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000 + 00222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFEFDFAD5A33BCB8800CD8D07F5E6C9FFFFFFFFFFFFEBF7EC9B + D7A1B6E2BCF5FBF6FFFFFFFFFFFF000000000000111111999999666666000000 + 000000111111AAAAAA555555000000222222FFFFFFBBBBBB000000444444FFFF + FFFFFFFF111111000000555555BBBBBB333333000000555555FFFFFF44444400 + 0000111111999999888888FFFFFF555555000000111111AAAAAA666666000000 + 000000CCCCCCFFFFFF444444000000000000EEEEEEFFFFFFFFFFFFFFFFFFFFFF + FFEEEEEE000000000000444444FFFFFF888888000000000000BBBBBBFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF222222000000111111CCCCCC + 000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFF + FFFFFFFFBBBBBB111111000000000000DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF1E1C0CA8904CB8800D4951AF8 + F1E0FFFFFFD4EED67AC97DBDE4C0FFFFFFFFFFFFFFFFFFFFFFFF000000000000 + 111111000000000000000000555555000000000000000000000000777777FFFF + FFBBBBBB000000444444FFFFFFFFFFFF99999900000000000000000000000000 + 0000BBBBBBFFFFFF444444000000000000000000000000DDDDDDDDDDDD111111 + 000000000000000000000000777777FFFFFFFFFFFF777777000000000000AAAA + AAFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000777777FFFFFF88888800 + 0000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + 777777000000000000444444000000000000888888FFFFFFFFFFFFFFFFFFFFFF + FF000000000000444444FFFFFFFFFFFFFFFFFF777777000000000000BBBBBBFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFE3 + C07CC98700CA8800DC9723FBFBF6C6EACB62BE62BEE4BFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFF888888888888CCCCCC555555444444888888FFFFFFAAAAAA4444 + 44444444888888FFFFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFBB + BBBB555555444444555555BBBBBBFFFFFFFFFFFF999999888888888888444444 + 666666EEEEEEFFFFFFDDDDDD666666444444444444999999FFFFFFFFFFFFFFFF + FF999999000000000000444444FFFFFFFFFFFFFFFFFFFFFFFF44444400000000 + 0000999999FFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000000000000000000000000000CCCC + CCFFFFFFFFFFFFFFFFFFFFFFFF000000000000444444FFFFFFFFFFFFFFFFFF66 + 6666000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFD8A84AC88700D08703F0D4A4D3E8C752BD59A2D9A3 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEE111111000000000000666666DDDDDDEE + EEEE666666000000000000111111EEEEEEFFFFFF888888000000000000BBBBBB + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1111110000 + 00000000000000222222FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000033 + 3333BBBBBBBBBBBB888888000000000000000000DDDDDDFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD09827CB8600E5C685 + E6EDD55BBF5D77C573F8FCF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFDDDDDD888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88888800 + 0000000000000000000000000000000000000000000000888888FFFFFFFFFFFF + 888888000000000000BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF666666000000000000000000666666FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFF000000000000000000000000000000000000000000000000444444 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFCB8C0FE0A03EF1F2E269C66A54B94ECDECCEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB000000444444FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF666666000000000000000000000000000000000000 + 777777FFFFFFFFFFFFFFFFFF888888000000000000BBBBBBFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAAAAA000000000000000000AA + AAAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000 + 000000000000111111CCCCCCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFD69424EBE3C39BD18851B84884C877FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFBFDFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDDDD + 888888999999FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB666666 + 444444444444666666BBBBBBFFFFFFFFFFFFFFFFFFFFFFFFBBBBBB8888888888 + 88DDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFF888888888888888888FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF888888 + 888888888888888888888888888888999999EEEEEEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFEBD09ED3E5 + BF4FBB484FBA4CABC16CF9E9CFFFFFFFFFFFFFFFFFFFC7DAFAC9DCFBFEFEFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFEFDFB7FCA7053B7415DBE57B7AF4AEBAC47FFFFFFF0F5FF9A + BBF7A8C6F8F1F6FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFCFEFCF9FFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFDEECCF54B73E53B7416ABF5ECD + B048ECCE99E5E7ED6C9FFFA9C8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0DCAFDAA83ADE + B152E3BE70E8C98BEDD5A5F1E0BEF7ECD8FBF5EBFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA9 + DB9A56B53953B6426AC252F2EBCFE0E1EA4B8AFFBAC5DBFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFE3BD68CF8E00CE8C00CD8B00CC8900CB8800CB8803CA8909DAAE58 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFF89CB6F55B53955B73DBCE2B1DCE6EE447FFF8B9EC8 + E1B354F8E5C1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF8EFD7A027CF8E00CE8C00CD8B00CC8900 + CB8800CA8700C88500E9CE9AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF70C05055B637A4D996 + E1EBF34A7CFF688FE3CEA95CDA9200D79C17F2DEAFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFECD093D08F00 + CF8E00CE8C00CD8B00CC8900CB8800CA8700D6A440FDFBF6FFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFF60B93F79C857E8F3EC658AF63C71FCAFA495DB9409D19100D39200D698 + 08EBCD85FEFCF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFAF4E5D79D1FD08F00CF8E00CE8C00CD8B00CC8900CB8800CD8E0EF3E5 + C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFF6ABF40CFE8CE94AEF03E6DFA7A9FEBD9C0 + 84D88F00D19100D39200D59500D69700E3B548F8EBCDFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3B95CD19000D08F00CF8E00CE8C00CD8B + 00CC8900CA8801EBD2A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFB8E0A6CADC + E5446AFB3F6DF884B6CEE2E9C7DE9F2AD29000D39200D59500D69700D89800DC + A214EDCE82FCF6E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBCE8BD39200D190 + 00D48E00D58C00D28B00CF8A00CC8900E9C483FFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFCFDFC7891FA406BF04D77F774B5A29BD790E9E2BCDA9107D3 + 9300D59500D69700D89800D99A00DB9C00E1AC28F0D48DFCF5E3FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFEFD599D59605D39200D5940AE3CB86E0CE91DEBE6FDCAE50EFCB94FFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFD9E2F4456AF3406BEF5D85F07D + BD9350C052CFE4BEF2D39DD69202D59500D69700D89800D99A00DB9C00DC9D00 + DD9F00E3AA18EFD07EFEFDF8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFEECCF87D79806D49400D49100DFA739E0EACE92D394A9 + D599BDDAA5D2E2B9D2EAC9DFF2DDEEF8EBF6FBF3FFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFA4 + B4FA4068EC406BF0648BED82C29358BC4B5DC26BEAEFDAF4D8A7DA990CD69700 + D89800D99A00DB9C00DC9D00DD9F00E7B73DF6E4B5FFFFFEFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF7EAE7C063D79800D69600D49400D7 + 9100E8CF90B2D9A54FBC5A51BA5150B94E4DB94852B7415BB84166BC4896D07B + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFF7B93F24068ED406BEF6389F18BC59F58BC4A56BA58 + 6AC97EF0F4E5F8E6C4E1A82ED89800D99A00DB9C00E0A71BF0D691FEFDF9FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E5BCE1AF36D8 + 9800D79800D69600D49400DE9814EFECD276CB8157B95456B84E56B74A56B644 + 55B63F55B53A55B432B4DEA1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF5F7CEF4068ED406CED + 557DF79BC8BB58BD4857BA5857BB5D65C77EE6EDD5FCF8EFF0C36EDFA114E9C1 + 60FAF2DCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 + EFD6EAC465DB9F08D99A00D89800D79800D59600D79504F0CD8FCFE5C251BC5D + 57B95456B84E56B74A56B64455B63F55B5397AC45EF8FCF6FFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFF5372EE4068ED406DED426EF8A5C7D86BC55D57BA5858BB5C59BC625FC4 + 76C6E5C4FFFFFFFBF5E7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFF9EED0ECC96FE0A717DC9D00DA9C00D99A00D89800D89A05E3B44B + F6DFB6F8F9F26EC87A57BA5957B95456B84E56B74A56B64455B63F5CB842D3EC + CAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFF5776EE4068ED406CED416FF28AABEDA0D4 + A658BC5158BB5D59BC625BBE6759C06F8ED9AAEFF8EEFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFDFAF0F6E2AFEAC156E1A710DEA000DD9E00DC9D00DA9C00 + DDA419E8C36BF7E8C6FFFFFFFFFFFF96DAA658BB5D57BA5857B95456B84E56B7 + 4A56B64455B63FB8E1ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFF6B86F04068 + ED406CED4170EE537DF9CBE1E46EC76658BB5C59BC625BBE675CBF6C5EC0706F + CB8ABAE6CAF7FCF9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFEFAF2F8E8BEF0D17EE8B633E1A400E0A200DFA100 + DEA000DFA30EE6B948EFD490FAF1DCFFFFFFFFFFFFFFFFFFBBE7C659BD6158BB + 5C57BB5558BB4E56B94A56B84956B643A2D990FFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFF8CA1F44068ED406CED4170ED4273F298B6F2C7E7CB5BBF5759 + BC635BBE675CBF6C5EC0705FC17461C27985D19AC6E9D0F5FBF7FFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFEFDFCF5E3F8E9C2F3D893EDC55DE6B023E2A400E2A400 + E2A400E2A607E5B22BEAC25AF2D999F9EFD4FEFDFAFFFFFFFFFFFFFFFFFFFFFF + FFC5EAD062C06D59BC605DBE60A1D7A7A5D7AC92D09084CB77AEDE9DFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFBECAF94068ED406CED4170ED42 + 75EE4678F7C6DCE9B9E4B45ABD5E5ABE685CBF6C5EC0705FC17461C27963C47E + 65C5827BCD95B7E4C6FCFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFCF7E8FAF0D5FBF2DAFBF2DBFBF2DAFAF0D6FA + EED0F9EBC8F7E6BAF5DFA6F2D68DEFCC6FEBC04EE7B228E4AB15E3A605E2A400 + E3A708E6AF1FEABD47EFCC71F4DDA0F9EECFFDF8EDFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFB9E5C461C16F5ABD6559BC5F7BCB77D4E6EE95B6F4A5 + C3EAB2CDE7CADCEDD8E1FBE6EBFEF0F3FEF7F8FEFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFF0 + F3FE587BEF406CED4170ED4275EE4378EE5785FDE5F0F4BEE7BB5EC0665CBF6D + 5EC0705FC17461C27963C47E65C58286D19ECFEDD9FEFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF0D4F5DEA3F3 + DA96F1D487F0CE77EEC969ECC55CEBC04EEABE48EABE4AEBC04EECC255EDC55D + EFCC70F2D589F5DFA5F8E8BEFAF0D6FDFAF1FFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FCF9A2DBB05CBF6D5BBE695ABD655A + BD5CB1DEB7A5C4EE447AFA4378F64274F7416FF8426FF14F78ED5F80F08CA1F4 + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFF9BB0F6406CED4170ED4275EE4378EE447CF0 + 6C97FBF1F7F9CDEDCD71C9785EC0705FC17461C2796FC888B2E2C1F7FCF8FFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFCFEFDF8FEFCF6FEFCF6FEFCF7FEFDF9 + FFFEFBFFFEFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFF3E48CD39B5E + BF715CBF6D5BBE695ABD6662C35FDDEEE7749FF9457FEF447BEF4377EE4273EE + 416FED406CED4068EDA1B3F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFEDF1FD5D82F0 + 4170ED4275EE4378EE447CEF467FF06795FDE0EEF3F1F9F39CDAA269C57C96D6 + A5E5F5E9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED + F8F1AADFB86BC6805EC1735EBF705CBF6D5BBE695BBD64B1E0B2C9DFEC477FF6 + 457FEF447BEF4377EE4273EE416FED406CED6283F0F5F7FEFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFC2D1F94472ED4275EE4378EE447CEF467FEF4784F05A90 + F9C4DAFAFFFFFFE7F7E8FCFEFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFE9F7EDB0E1C078CC9062C37C61C2785EC1735EBF705CBF6E84CF8C + C9EBC6F5FAF96B9AFC4681F0457FEF447BEF4377EE4273EE416FED4671EEC8D4 + FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9BB4F64275EE4378 + EE447CEF467FEF4784EF4A87F04C8AF599BFF6F5F9FDFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFCFEFDDCF2E4A7DEB975CB9065C58364C48062C37C61C278 + 67C57B94D5A0CFECD3FAFDFAFFFFFF9EC0F54884F24682EF457FEF447BEF4377 + EE4273EE416FEDA2B8F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFAFBFF87A7F44378EE447CEF467FEF4784EF4A87F04B8BF04E8FF36C + A2F8C2DAFDFCFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFBFDFCE4F5E9BEE7CC93D6AA6FC98D6AC78867C685 + 65C58368C68384D098AFE0BBDFF3E3FFFFFFFFFFFFFFFFFFC3D7FF4E8AF44885 + EF4682EF457FEF447BEF4377EE4273EE8AA6F4FBFCFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8FAFE88AAF4447CEF467FEF47 + 84EF4A87F04B8BF14E90F14F92F05297F282B5F5CAE0FBFAFCFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFF6FCF8E2F4E8CAEBD6ABDFBD89D3A26DC88C6DC88C + 6DC88C6CC88B7DCE9795D7AAB9E5C7DDF2E3FBFDFBFFFFFFFFFFFFFFFFFFFFFF + FFCCDFFD5895F14A88F04885F04682EF457FEF447BEF4377EE8CAAF5F8FAFEFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFBFCFF9DBAF74881EF4784EF4A87F04B8BF14E90F14F92F05297F2549AF2 + 579DF37AB4F5B8D9FAFCFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFF4FBF6ECF8F0EEF9F1EEF9F1EDF8F1ECF8F0EA + F7EFE8F6EDE2F4E8D8F0E0CAEBD6B9E5C8A7DEBA8FD5A77BCD9771CA8F6DC88C + 6DC88C78CC958AD3A3A2DCB6BEE6CCDAF1E1EFF9F2FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFC2D8FC5997F24B8CF04A88F04885F04682EF457FEF48 + 7EEF9EB9F6FBFCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBCD1F95B92F14A87F04B8BF1 + 4E90F14F92F05297F2549AF2579DF36EAEF5C1DDFAFCFEFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4F5EAC6E9D2BE + E6CCB5E3C4ACE0BEA5DDB89EDAB296D8AC90D5A88CD4A590D5A895D7AC9BD9B0 + A3DCB6AFE1C0C0E7CDD1EEDBDFF3E6EEF9F1FDFEFEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFDFFAACBFA5595F04E90F14B8CF04A + 88F04885F04682EF588CF1C1D4FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFDFEAFC85AEF54B8BF14E90F14F92F05297F25C9FF3A3C9F8EFF6FEFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFCFEFCF9FDFAF8FCF9F6FCF8F6FCF8F6FCF8F8FCFA + FAFDFBFCFEFCFEFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F0FD8CBBF652 + 96F25092F04E90F14B8CF04A88F04885F080A9F4DCE7FCFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFFC2D8FA73A7F45495F081B3 + F6D6E7FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3 + F8FEB0D3F966A5F4549AF25296F25092F04E90F14B8CF06FA0F3C1D5FAFCFDFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFF3F8FECFE2FBF8FBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFF0F7FEB8D8F979B4F558A1F3569CF3549AF25296F25092F071A6F4 + B5D0F9F4F8FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFE2EFFDA9D1F972B3F55DA7F25BA4F358A1F3569CF3 + 579CF281B2F6C1D8FAF6F9FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFCFDFFE9F3FDC0DDFA90C3F767ADF45DA8F35DA8F3 + 5DA7F25EA6F372B0F5A0C7F8D5E6FCFDFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 + 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFEFEFFE9F3FDCFE5FBAED4F988BFF663ABF35DA8F3 + 5DA8F35DA8F368AEF485BDF6A9D0F8CFE5FCF5F9FEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFF4F9FEEDF5FEEEF6FEF0F7FEF1F7FEF0F7FEEE + F6FEEAF4FDE5F1FDDDEDFDCFE5FBBDDCFAA8D0F990C3F775B5F563ABF35DA8F3 + 5DA8F365ACF476B5F58DC2F7ACD3F9CCE3FBE9F3FDFAFCFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFEEFDBDDCFAB5 + D7FAACD2F9A2CDF898C8F78FC3F786BEF680BBF67BB8F577B6F57CB9F586BEF6 + 92C4F79ECBF8AED4F9C3DFFBD6E9FCE5F1FDF6FAFEFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + 0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000} + end + object Label1: TLabel + Left = 127 + Top = 24 + Width = 138 + Height = 16 + Alignment = taCenter + AutoSize = False + Caption = 'TPSQLUpdateSQL' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlack + Font.Height = -12 + Font.Name = 'Arial' + Font.Style = [fsBold] + ParentFont = False + Transparent = True + end + object Label2: TLabel + Left = 13 + Top = 173 + Width = 213 + Height = 13 + Caption = '(c) Pavlo Golub' + end + object VersionLabel: TLabel + Left = 130 + Top = 50 + Width = 135 + Height = 13 + Alignment = taCenter + AutoSize = False + Caption = 'V.' + Transparent = True + end + object Label5: TLabel + Left = 16 + Top = 96 + Width = 257 + Height = 41 + Alignment = taCenter + AutoSize = False + Caption = 'Direct Access Components for PostgreSQL(tm)' + Font.Charset = ANSI_CHARSET + Font.Color = clBlack + Font.Height = -16 + Font.Name = 'Arial' + Font.Style = [fsBold] + ParentFont = False + WordWrap = True + end + object RegLabel: TLabel + Left = 13 + Top = 152 + Width = 260 + Height = 13 + Alignment = taCenter + AutoSize = False + Font.Charset = DEFAULT_CHARSET + Font.Color = clMaroon + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [fsBold] + ParentFont = False + end + object Label3: TLabel + Left = 16 + Top = 192 + Width = 71 + Height = 13 + Cursor = crHandPoint + Caption = 'Ask for support' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + ParentFont = False + OnClick = SpeedButton1Click + end + object Label4: TLabel + Left = 16 + Top = 208 + Width = 72 + Height = 13 + Cursor = crHandPoint + Caption = 'Check updates' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + ParentFont = False + OnClick = SpeedButton2Click + end + object Label6: TLabel + Left = 16 + Top = 224 + Width = 43 + Height = 13 + Cursor = crHandPoint + Caption = 'Buy Now' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + ParentFont = False + OnClick = SpeedButton3Click + end + object Button1: TButton + Left = 207 + Top = 248 + Width = 75 + Height = 25 + Anchors = [akRight] + Caption = 'OK' + ModalResult = 1 + TabOrder = 0 + end +end diff --git a/psqlAboutFrm.lrs b/Source/psqlAboutFrm.lrs similarity index 98% rename from psqlAboutFrm.lrs rename to Source/psqlAboutFrm.lrs index 2361bc6..63700ea 100644 --- a/psqlAboutFrm.lrs +++ b/Source/psqlAboutFrm.lrs @@ -1,2359 +1,2359 @@ -LazarusResources.Add('TPSQLAboutComp','FORMDATA',[ - 'TPF0'#14'TPSQLAboutComp'#13'PSQLAboutComp'#4'Left'#3#155#1#3'Top'#3#251#0#11 - +'BorderIcons'#11#12'biSystemMenu'#0#11'BorderStyle'#7#8'bsDialog'#7'Caption' - +#6#8'About...'#12'ClientHeight'#3#22#1#11'ClientWidth'#3#31#1#5'Color'#7#9'c' - +'lBtnFace'#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color'#7#12'clWind' - +'owText'#11'Font.Height'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.Styl' - +'e'#11#0#8'Position'#7#14'poScreenCenter'#8'OnCreate'#7#10'FormCreate'#13'Pi' - +'xelsPerInch'#2'`'#0#6'TBevel'#6'Bevel1'#4'Left'#2#8#3'Top'#2#8#5'Width'#3#17 - +#1#6'Height'#3#233#0#5'Shape'#7#7'bsFrame'#0#0#6'TImage'#6'Image1'#4'Left'#2 - +#13#3'Top'#2#14#5'Width'#3#4#1#6'Height'#2';'#8'AutoSize'#9#6'Center'#9#12'P' - +'icture.Data'#10#6#180#0#0#7'TBitmap'#250#179#0#0'BM'#250#179#0#0#0#0#0#0'6' - +#0#0#0'('#0#0#0#4#1#0#0';'#0#0#0#1#0#24#0#0#0#0#0#196#179#0#0#18#11#0#0#18#11 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#253#251#245#239#213#155#242#223#177#255#255#254#255#255#255#187 - +#187#187#187#187#187#255#255#255#255#255#255#255#255#255#187#187#187#187#187 - +#187#255#255#255#255#255#255#255#255#255#187#187#187#187#187#187#255#255#255 - +#238#238#238#187#187#187#204#204#204#255#255#255#255#255#255#255#255#255#238 - +#238#238#136#136#136#136#136#136#136#136#136#238#238#238#255#255#255#255#255 - +#255#204#204#204#187#187#187#238#238#238#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#153#153#153#136#136#136#136#136#136#221#221#221#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#238#238 - +#238#153#153#153#136#136#136#136#136#136#153#153#153#238#238#238#255#255#255 - +#255#255#255#255#255#255#255#255#255#221#221#221#187#187#187#187#187#187#187 - +#187#187#187#187#187#187#187#187#187#187#187#187#187#187#187#187#187#221#221 - +#221#187#187#187#187#187#187#221#221#221#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#204#204#204#187#187#187#187#187#187#238#238#238#255 - +#255#255#187#187#187#187#187#187#204#204#204#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#247#237 - +#213#227#186'^'#238#211#150#253#251#244#255#255#255#255#255#255#0#0#0#0#0#0 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 - +#255#255#0#0#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255#255 - +#255#204#204#204#17#17#17#0#0#0#0#0#0#0#0#0#17#17#17#204#204#204#255#255#255 - +'DDD'#0#0#0#187#187#187#255#255#255#255#255#255#255#255#255#238#238#238'333' - +#0#0#0#0#0#0#0#0#0#0#0#0#153#153#153#255#255#255#255#255#255#255#255#255#255 - +#255#255#153#153#153#17#17#17#0#0#0#0#0#0#0#0#0#0#0#0#17#17#17#153#153#153 - +#255#255#255#255#255#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#153#153#153#0#0#0#0#0#0'fff'#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255'"""'#0#0#0#0#0#0#221#221#221#255#255#255#0#0 - +#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#241#221#177#217#165'.'#240#219#170#255#255#255#255 - +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255 - +#187#187#187#0#0#0'DDD'#255#255#255#255#255#255'333'#0#0#0'"""'#136#136#136 - +#17#17#17#0#0#0'DDD'#255#255#255'DDD'#0#0#0#187#187#187#255#255#255#255#255 - +#255#255#255#255'www'#0#0#0#0#0#0'www333'#0#0#0#17#17#17#221#221#221#255#255 - +#255#255#255#255#187#187#187#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#187#187#187#255#255#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#238#238#238#0#0#0#0#0#0#17#17#17#255#255#255#255#255#255 - +#255#255#255#255#255#255#204#204#204#0#0#0#0#0#0'333'#255#255#255#255#255#255 - +#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#236#210#155#210#148#12#239#218#169#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 - +#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255 - +#255#187#187#187#0#0#0'DDD'#255#255#255#204#204#204#0#0#0#17#17#17#221#221 - +#221#255#255#255#221#221#221#0#0#0#0#0#0#221#221#221'DDD'#0#0#0#187#187#187 - +#255#255#255#255#255#255#255#255#255#17#17#17#0#0#0#136#136#136#255#255#255 - +#238#238#238'"""'#0#0#0#136#136#136#255#255#255#255#255#255'"""'#0#0#0#0#0#0 - +'333'#153#153#153#153#153#153'333'#0#0#0#0#0#0'"""'#255#255#255#255#255#255 - +#136#136#136#0#0#0#0#0#0'fff'#136#136#136#136#136#136#136#136#136#136#136#136 - +#136#136#136#255#255#255'UUU'#0#0#0#0#0#0#204#204#204#255#255#255#255#255#255 - +#255#255#255#136#136#136#0#0#0#0#0#0'www'#255#255#255#255#255#255#0#0#0#0#0#0 - +'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#237#213 - +#161#207#141#0#231#197'|'#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#187#187#187 - +#0#0#0'DDD'#255#255#255#136#136#136#0#0#0'UUU'#255#255#255#255#255#255#255 - +#255#255'wwwfff'#221#221#221'DDD'#0#0#0#187#187#187#255#255#255#255#255#255 - +#204#204#204#0#0#0#17#17#17#255#255#255#255#255#255#255#255#255#136#136#136#0 - +#0#0'UUU'#255#255#255#170#170#170#0#0#0#0#0#0'"""'#238#238#238#255#255#255 - +#255#255#255#238#238#238'"""'#0#0#0#0#0#0#170#170#170#255#255#255#136#136#136 - +#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#204#204#204#255#255#255#255#255#255#0#0#0#0#0#0'DDD'#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#240#220#178#206#143#7#218#169'<'#254 - +#252#248#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255 - +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255 - +#255#255#136#136#136#0#0#0#136#136#136#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255'DDD'#0#0#0#187#187#187#255#255#255#255 - +#255#255#187#187#187#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#187#187 - +#187#0#0#0'DDD'#255#255#255#136#136#136#0#0#0#0#0#0#153#153#153#255#255#255 - +#255#255#255#255#255#255#255#255#255#153#153#153#0#0#0#0#0#0#136#136#136#255 - +#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#221#221#221#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#17#17#17#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0 - ,#255#255#255#255#255#255#255#255#255#255#255#255#247#237#216#209#150#26#206 - +#140#1#243#226#190#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255 - +#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#187 - +#187#187#0#0#0'DDD'#255#255#255#136#136#136#0#0#0#136#136#136#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255'DDD'#0#0#0#187#187 - +#187#255#255#255#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255#255#255 - +#255#255#255#187#187#187#0#0#0'DDD'#255#255#255'DDD'#0#0#0#0#0#0#221#221#221 - +#255#255#255#255#255#255#255#255#255#255#255#255#221#221#221#0#0#0#0#0#0'DDD' - +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255'333'#0#0#0#0#0#0'33' - +'3DDD"""'#0#0#0#0#0#0'fff'#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#17 - +#17#17'DDDDDDDDDwww'#221#221#221#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255 - +#254#252#248#218#171'L'#204#138#0#219#171'D'#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#250#253#250#255#255#255#255#255#255#0#0#0#0#0#0 - +#238#238#238#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 - +#255#255#0#0#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#136#136 - +#136#0#0#0'fff'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255'DDD'#0#0#0#187#187#187#255#255#255#255#255#255#187#187#187#0#0#0 - +'"""'#255#255#255#255#255#255#255#255#255#153#153#153#0#0#0'DDD'#255#255#255 - +'DDD'#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#0#0#0#0#0#0'DDD'#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#136#136#136#0#0#0#0#0#0#153#153#153#255#255#255'fff'#0#0#0#0#0#0#153 - +#153#153#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#17#17#17#204#204#204#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#237#215#170 - +#203#136#0#204#138#0#234#206#147#255#255#255#255#255#255#255#255#255#253#254 - +#253#196#232#201#204#235#209#254#254#254#255#255#255#0#0#0#0#0#0#153#153#153 - +#255#255#255#221#221#221#0#0#0#0#0#0#170#170#170#255#255#255#238#238#238#0#0 - +#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#187#187#187#0#0#0'""' - +'"'#238#238#238#255#255#255#221#221#221#0#0#0'333'#255#255#255'DDD'#0#0#0'ww' - +'w'#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#187#187#187#255#255#255 - +#255#255#255'DDD'#0#0#0#136#136#136#255#255#255'DDD'#0#0#0#0#0#0#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0'DDD' - +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#204#204#204#0#0#0#0 - +#0#0'UUU'#255#255#255#17#17#17#0#0#0#0#0#0#238#238#238#255#255#255#255#255 - +#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'"""'#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255 - +#255#255#255#254#253#250#213#163';'#203#136#0#205#141#7#245#230#201#255#255 - +#255#255#255#255#235#247#236#155#215#161#182#226#188#245#251#246#255#255#255 - +#255#255#255#0#0#0#0#0#0#17#17#17#153#153#153'fff'#0#0#0#0#0#0#17#17#17#170 - +#170#170'UUU'#0#0#0'"""'#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255 - +#255#255#17#17#17#0#0#0'UUU'#187#187#187'333'#0#0#0'UUU'#255#255#255'DDD'#0#0 - +#0#17#17#17#153#153#153#136#136#136#255#255#255'UUU'#0#0#0#17#17#17#170#170 - +#170'fff'#0#0#0#0#0#0#204#204#204#255#255#255'DDD'#0#0#0#0#0#0#238#238#238 - +#255#255#255#255#255#255#255#255#255#255#255#255#238#238#238#0#0#0#0#0#0'DDD' - +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255'"""'#0#0 - +#0#17#17#17#204#204#204#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255 - +#255#255#255#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#187#187#187#17#17#17#0 - +#0#0#0#0#0#221#221#221#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255#241#225#192#202#137#4#203#136#0#212#149#26 - +#248#241#224#255#255#255#212#238#214'z'#201'}'#189#228#192#255#255#255#255 - +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#17#17#17#0#0#0#0#0#0#0#0#0'UUU' - +#0#0#0#0#0#0#0#0#0#0#0#0'www'#255#255#255#187#187#187#0#0#0'DDD'#255#255#255 - +#255#255#255#153#153#153#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#187#187#187#255#255 - +#255'DDD'#0#0#0#0#0#0#0#0#0#0#0#0#221#221#221#221#221#221#17#17#17#0#0#0#0#0 - +#0#0#0#0#0#0#0'www'#255#255#255#255#255#255'www'#0#0#0#0#0#0#170#170#170#255 - +#255#255#255#255#255#255#255#255#255#255#255#170#170#170#0#0#0#0#0#0'www'#255 - +#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255'www'#0#0#0#0 - +#0#0'DDD'#0#0#0#0#0#0#136#136#136#255#255#255#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255'www'#0#0#0#0#0#0 - +#187#187#187#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 - +#255#255#255#255#255#227#192'|'#201#135#0#202#136#0#220#151'#'#251#251#246 - +#198#234#203'b'#190'b'#190#228#191#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#136#136#136#136#136#136#204#204#204'UUUDDD'#136#136#136 - +#255#255#255#170#170#170'DDDDDD'#136#136#136#255#255#255#255#255#255#221#221 - +#221#136#136#136#153#153#153#255#255#255#255#255#255#255#255#255#187#187#187 - ,'UUUDDDUUU'#187#187#187#255#255#255#255#255#255#153#153#153#136#136#136#136 - +#136#136'DDDfff'#238#238#238#255#255#255#221#221#221'fffDDDDDD'#153#153#153 - +#255#255#255#255#255#255#255#255#255#153#153#153#0#0#0#0#0#0'DDD'#255#255#255 - +#255#255#255#255#255#255#255#255#255'DDD'#0#0#0#0#0#0#153#153#153#255#255#255 - +#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#187#187#187#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#204#204#204#255#255#255#255#255#255#255#255#255#255 - +#255#255#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255'fff'#0#0#0#0#0 - +#0#187#187#187#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0 - +#255#255#255#255#255#255#216#168'J'#200#135#0#208#135#3#240#212#164#211#232 - +#199'R'#189'Y'#162#217#163#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#238#238#238#17 - +#17#17#0#0#0#0#0#0'fff'#221#221#221#238#238#238'fff'#0#0#0#0#0#0#17#17#17#238 - +#238#238#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#17#17#17#0#0#0#0#0#0#0#0#0'"""'#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#0#0#0#0#0#0'333'#187#187#187#187#187#187#136#136 - +#136#0#0#0#0#0#0#0#0#0#221#221#221#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#0#0#0#0#0#0#255#255#255#255#255#255#208#152''''#203#134#0#229#198 - +#133#230#237#213'['#191']w'#197's'#248#252#248#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#221#221#221#136#136#136#153 - +#153#153#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#136#136#136#255#255#255#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255'fff'#0#0#0#0#0#0#0#0#0'fff'#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#0#0#0#0#0#0#255#255#255#255#255#255#203#140#15#224#160'>'#241#242#226'i'#198 - +'jT'#185'N'#205#236#206#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255'fff'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'www'#255#255#255#255#255#255 - +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#170#170#170#0#0#0#0#0#0#0#0#0#170#170#170#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#17 - +#17#17#204#204#204#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0#255#255#255#255#255#255#214#148'$'#235#227#195#155#209#136 - +'Q'#184'H'#132#200'w'#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#251#253#255#254#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#221#221#221#136#136#136#153#153 - +#153#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#187#187#187'fffDDDDDDfff'#187#187#187 - +#255#255#255#255#255#255#255#255#255#255#255#255#187#187#187#136#136#136#136 - +#136#136#221#221#221#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#136#136#136 - +#136#136#136#136#136#136#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#136#136#136#136#136#136#136#136#136#136#136#136#136#136 - +#136#136#136#136#153#153#153#238#238#238#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#235 - +#208#158#211#229#191'O'#187'HO'#186'L'#171#193'l'#249#233#207#255#255#255#255 - ,#255#255#255#255#255#199#218#250#201#220#251#254#254#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#254#253#251#127#202 - +'pS'#183'A]'#190'W'#183#175'J'#235#172'G'#255#255#255#240#245#255#154#187#247 - +#168#198#248#241#246#254#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#254#254#252#254#252#249#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#0#0#0#0#0#0#255#255#255#255#255#255#222#236#207'T'#183'>S'#183'Aj'#191'^' - +#205#176'H'#236#206#153#229#231#237'l'#159#255#169#200#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#240#220#175#218 - +#168':'#222#177'R'#227#190'p'#232#201#139#237#213#165#241#224#190#247#236#216 - +#251#245#235#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255 - +#255#255#255#255#169#219#154'V'#181'9S'#182'Bj'#194'R'#242#235#207#224#225 - +#234'K'#138#255#186#197#219#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#227#189'h'#207#142#0#206#140#0#205 - +#139#0#204#137#0#203#136#0#203#136#3#202#137#9#218#174'X'#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#137#203'oU'#181 - +'9U'#183'='#188#226#177#220#230#238'D'#127#255#139#158#200#225#179'T'#248#229 - +#193#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#252#248 - +#239#215#160''''#207#142#0#206#140#0#205#139#0#204#137#0#203#136#0#202#135#0 - +#200#133#0#233#206#154#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255'p'#192'PU'#182'7'#164#217#150#225#235#243'J|' - +#255'h'#143#227#206#169'\'#218#146#0#215#156#23#242#222#175#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#236#208#147#208#143#0#207#142#0#206#140 - +#0#205#139#0#204#137#0#203#136#0#202#135#0#214#164'@'#253#251#246#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255'`'#185 - +'?y'#200'W'#232#243#236'e'#138#246'm'#250'z' - +#159#235#217#192#132#216#143#0#209#145#0#211#146#0#213#149#0#214#151#0#227 - +#181'H'#248#235#205#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#227#185'\'#209#144#0#208#143#0#207#142#0#206#140#0 - +#205#139#0#204#137#0#202#136#1#235#210#160#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#184 - +#224#166#202#220#229'Dj'#251'?m'#248#132#182#206#226#233#199#222#159'*'#210 - +#144#0#211#146#0#213#149#0#214#151#0#216#152#0#220#162#20#237#206#130#252#246 - +#231#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#235#206#139#211#146#0#209 - +#144#0#212#142#0#213#140#0#210#139#0#207#138#0#204#137#0#233#196#131#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255#252#253#252'x'#145#250'@k'#240'Mw'#247't' - +#181#162#155#215#144#233#226#188#218#145#7#211#147#0#213#149#0#214#151#0#216 - +#152#0#217#154#0#219#156#0#225#172'('#240#212#141#252#245#227#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#239#213 - +#153#213#150#5#211#146#0#213#148#10#227#203#134#224#206#145#222#190'o'#220 - +#174'P'#239#203#148#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#217 - +#226#244'Ej'#243'@k'#239']'#133#240'}'#189#147'P'#192'R'#207#228#190#242#211 - +#157#214#146#2#213#149#0#214#151#0#216#152#0#217#154#0#219#156#0#220#157#0 - +#221#159#0#227#170#24#239#208'~'#254#253#248#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#254#236#207#135#215#152#6#212#148#0#212#145#0#223#167'9'#224#234 - +#206#146#211#148#169#213#153#189#218#165#210#226#185#210#234#201#223#242#221 - +#238#248#235#246#251#243#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255#164#180#250'@h'#236'@k'#240'd'#139#237#130#194 - +#147'X'#188'K]'#194'k'#234#239#218#244#216#167#218#153#12#214#151#0#216#152#0 - +#217#154#0#219#156#0#220#157#0#221#159#0#231#183'='#246#228#181#255#255#254 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#252#247#234#231#192'c'#215#152#0#214#150#0#212#148#0 - +#215#145#0#232#207#144#178#217#165'O'#188'ZQ'#186'QP'#185'NM'#185'HR'#183'A[' - +#184'Af'#188'H'#150#208'{'#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255'{'#147#242'@h'#237'@k'#239'c'#137#241#139 - +#197#159'X'#188'JV'#186'Xj'#201'~'#240#244#229#248#230#196#225#168'.'#216#152 - +#0#217#154#0#219#156#0#224#167#27#240#214#145#254#253#249#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#245#229#188#225#175'6'#216#152#0#215#152#0#214#150#0#212#148 - +#0#222#152#20#239#236#210'v'#203#129'W'#185'TV'#184'NV'#183'JV'#182'DU'#182 - +'?U'#181':U'#180'2'#180#222#161#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#0#0#0#0#0#0#255#255#255#255#255#255'_|'#239'@h'#237'@l'#237'U}'#247#155#200 - +#187'X'#189'HW'#186'XW'#187']e'#199'~'#230#237#213#252#248#239#240#195'n'#223 - +#161#20#233#193'`'#250#242#220#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#249#239 - +#214#234#196'e'#219#159#8#217#154#0#216#152#0#215#152#0#213#150#0#215#149#4 - +#240#205#143#207#229#194'Q'#188']W'#185'TV'#184'NV'#183'JV'#182'DU'#182'?U' - +#181'9z'#196'^'#248#252#246#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255'Sr'#238'@h'#237'@m'#237'Bn'#248#165#199#216 - +'k'#197']W'#186'XX'#187'\Y'#188'b_'#196'v'#198#229#196#255#255#255#251#245 - +#231#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#249#238#208#236#201'o'#224#167#23 - +#220#157#0#218#156#0#217#154#0#216#152#0#216#154#5#227#180'K'#246#223#182#248 - +#249#242'n'#200'zW'#186'YW'#185'TV'#184'NV'#183'JV'#182'DU'#182'?\'#184'B' - ,#211#236#202#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255'Wv'#238'@h'#237'@l'#237'Ao'#242#138#171#237#160 - +#212#166'X'#188'QX'#187']Y'#188'b['#190'gY'#192'o'#142#217#170#239#248#238 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#253#250#240#246#226#175#234#193'V'#225#167#16#222#160#0#221#158#0#220#157#0 - +#218#156#0#221#164#25#232#195'k'#247#232#198#255#255#255#255#255#255#150#218 - +#166'X'#187']W'#186'XW'#185'TV'#184'NV'#183'JV'#182'DU'#182'?'#184#225#173 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255'k'#134#240'@h'#237'@l'#237'Ap'#238'S}'#249#203 - +#225#228'n'#199'fX'#187'\Y'#188'b['#190'g\'#191'l^'#192'po'#203#138#186#230 - +#202#247#252#249#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#254#250#242#248#232#190#240#209'~'#232#182 - +'3'#225#164#0#224#162#0#223#161#0#222#160#0#223#163#14#230#185'H'#239#212#144 - +#250#241#220#255#255#255#255#255#255#255#255#255#187#231#198'Y'#189'aX'#187 - +'\W'#187'UX'#187'NV'#185'JV'#184'IV'#182'C'#162#217#144#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 - +#255#255#255#255#255#140#161#244'@h'#237'@l'#237'Ap'#237'Bs'#242#152#182#242 - +#199#231#203'['#191'WY'#188'c['#190'g\'#191'l^'#192'p_'#193'ta'#194'y'#133 - +#209#154#198#233#208#245#251#247#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#254#253#252#245#227#248#233#194 - +#243#216#147#237#197']'#230#176'#'#226#164#0#226#164#0#226#164#0#226#166#7 - +#229#178'+'#234#194'Z'#242#217#153#249#239#212#254#253#250#255#255#255#255 - +#255#255#255#255#255#255#255#255#197#234#208'b'#192'mY'#188'`]'#190'`'#161 - +#215#167#165#215#172#146#208#144#132#203'w'#174#222#157#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255#190#202#249'@h'#237'@l'#237'Ap'#237'Bu'#238 - +'Fx'#247#198#220#233#185#228#180'Z'#189'^Z'#190'h\'#191'l^'#192'p_'#193'ta' - +#194'yc'#196'~e'#197#130'{'#205#149#183#228#198#252#254#253#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#252#247#232#250#240#213#251#242#218 - +#251#242#219#251#242#218#250#240#214#250#238#208#249#235#200#247#230#186#245 - +#223#166#242#214#141#239#204'o'#235#192'N'#231#178'('#228#171#21#227#166#5 - +#226#164#0#227#167#8#230#175#31#234#189'G'#239#204'q'#244#221#160#249#238#207 - +#253#248#237#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#185#229#196'a'#193'oZ'#189'eY'#188'_{'#203'w'#212#230 - +#238#149#182#244#165#195#234#178#205#231#202#220#237#216#225#251#230#235#254 - +#240#243#254#247#248#254#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 - +#0#0#255#255#255#255#255#255#240#243#254'X{'#239'@l'#237'Ap'#237'Bu'#238'Cx' - +#238'W'#133#253#229#240#244#190#231#187'^'#192'f\'#191'm^'#192'p_'#193'ta' - +#194'yc'#196'~e'#197#130#134#209#158#207#237#217#254#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#250#240#212#245#222#163#243#218#150 - +#241#212#135#240#206'w'#238#201'i'#236#197'\'#235#192'N'#234#190'H'#234#190 - +'J'#235#192'N'#236#194'U'#237#197']'#239#204'p'#242#213#137#245#223#165#248 - +#232#190#250#240#214#253#250#241#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#248#252#249#162#219#176'\'#191'm['#190'iZ'#189'eZ'#189'\'#177#222#183#165 - +#196#238'Dz'#250'Cx'#246'Bt'#247'Ao'#248'Bo'#241'Ox'#237'_'#128#240#140#161 - +#244#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 - +#255#255#255#255#255#155#176#246'@l'#237'Ap'#237'Bu'#238'Cx'#238'D|'#240'l' - +#151#251#241#247#249#205#237#205'q'#201'x^'#192'p_'#193'ta'#194'yo'#200#136 - +#178#226#193#247#252#248#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#254#252 - +#254#253#248#254#252#246#254#252#246#254#252#247#254#253#249#255#254#251#255 - +#254#253#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#223#243#228#140 - +#211#155'^'#191'q\'#191'm['#190'iZ'#189'fb'#195'_'#221#238#231't'#159#249'E' - +#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237'@l'#237'@h'#237#161#179#246#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 - +#255#255#237#241#253']'#130#240'Ap'#237'Bu'#238'Cx'#238'D|'#239'F'#127#240'g' - +#149#253#224#238#243#241#249#243#156#218#162'i'#197'|'#150#214#165#229#245 - +#233#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#237#248#241#170#223#184'k'#198#128 - +'^'#193's^'#191'p\'#191'm['#190'i['#189'd'#177#224#178#201#223#236'G'#127#246 - +'E'#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237'@l'#237'b'#131#240#245#247#254 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255 - +#255#255#255#255#255#255#255#194#209#249'Dr'#237'Bu'#238'Cx'#238'D|'#239'F' - +#127#239'G'#132#240'Z'#144#249#196#218#250#255#255#255#231#247#232#252#254 - +#252#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#233#247#237#176#225#192'x'#204#144'b'#195'|a' - +#194'x^'#193's^'#191'p\'#191'n'#132#207#140#201#235#198#245#250#249'k'#154 - +#252'F'#129#240'E'#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237'Fq'#238#200#212 - ,#250#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#155#180#246'Bu'#238 - +'Cx'#238'D|'#239'F'#127#239'G'#132#239'J'#135#240'L'#138#245#153#191#246#245 - +#249#253#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#252#254#253#220#242#228#167#222#185'u'#203#144'e'#197#131'd'#196#128 - +'b'#195'|a'#194'xg'#197'{'#148#213#160#207#236#211#250#253#250#255#255#255 - +#158#192#245'H'#132#242'F'#130#239'E'#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237 - +#162#184#246#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#250#251#255#135#167#244'Cx'#238'D|'#239'F'#127#239'G'#132#239'J'#135#240'K' - +#139#240'N'#143#243'l'#162#248#194#218#253#252#253#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#251 - +#253#252#228#245#233#190#231#204#147#214#170'o'#201#141'j'#199#136'g'#198#133 - +'e'#197#131'h'#198#131#132#208#152#175#224#187#223#243#227#255#255#255#255 - +#255#255#255#255#255#195#215#255'N'#138#244'H'#133#239'F'#130#239'E'#127#239 - +'D{'#239'Cw'#238'Bs'#238#138#166#244#251#252#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#248#250#254#136#170#244'D|' - +#239'F'#127#239'G'#132#239'J'#135#240'K'#139#241'N'#144#241'O'#146#240'R'#151 - +#242#130#181#245#202#224#251#250#252#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#246#252#248#226 - +#244#232#202#235#214#171#223#189#137#211#162'm'#200#140'm'#200#140'm'#200#140 - +'l'#200#139'}'#206#151#149#215#170#185#229#199#221#242#227#251#253#251#255 - +#255#255#255#255#255#255#255#255#255#255#255#204#223#253'X'#149#241'J'#136 - +#240'H'#133#240'F'#130#239'E'#127#239'D{'#239'Cw'#238#140#170#245#248#250#254 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#251#252#255#157#186#247'H'#129#239'G'#132#239'J'#135 - +#240'K'#139#241'N'#144#241'O'#146#240'R'#151#242'T'#154#242'W'#157#243'z'#180 - +#245#184#217#250#252#254#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#244#251#246#236#248#240#238#249#241#238#249#241#237#248#241#236#248 - +#240#234#247#239#232#246#237#226#244#232#216#240#224#202#235#214#185#229#200 - +#167#222#186#143#213#167'{'#205#151'q'#202#143'm'#200#140'm'#200#140'x'#204 - +#149#138#211#163#162#220#182#190#230#204#218#241#225#239#249#242#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#194 - +#216#252'Y'#151#242'K'#140#240'J'#136#240'H'#133#240'F'#130#239'E'#127#239'H' - +'~'#239#158#185#246#251#252#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#188#209#249'['#146#241'J'#135#240'K'#139#241'N'#144#241'O' - +#146#240'R'#151#242'T'#154#242'W'#157#243'n'#174#245#193#221#250#252#254#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#228#245#234#198#233 - +#210#190#230#204#181#227#196#172#224#190#165#221#184#158#218#178#150#216#172 - +#144#213#168#140#212#165#144#213#168#149#215#172#155#217#176#163#220#182#175 - +#225#192#192#231#205#209#238#219#223#243#230#238#249#241#253#254#254#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#252#253#255#170#203#250'U'#149#240'N'#144#241'K'#140 - +#240'J'#136#240'H'#133#240'F'#130#239'X'#140#241#193#212#250#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#223#234#252#133#174#245'K'#139#241'N'#144#241'O'#146#240'R'#151#242'\' - +#159#243#163#201#248#239#246#254#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#252#254#252#249 - +#253#250#248#252#249#246#252#248#246#252#248#246#252#248#248#252#250#250#253 - +#251#252#254#252#254#255#254#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#229#240 - +#253#140#187#246'R'#150#242'P'#146#240'N'#144#241'K'#140#240'J'#136#240'H' - +#133#240#128#169#244#220#231#252#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#254#254#255#194#216#250's'#167#244'T'#149#240#129#179#246#214#231#252#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#243#248#254#176#211#249'f'#165#244'T'#154 - +#242'R'#150#242'P'#146#240'N'#144#241'K'#140#240'o'#160#243#193#213#250#252 - +#253#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#243#248#254#207#226#251#248#251#254#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#240 - +#247#254#184#216#249'y'#180#245'X'#161#243'V'#156#243'T'#154#242'R'#150#242 - +'P'#146#240'q'#166#244#181#208#249#244#248#254#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#226#239#253#169#209#249'r'#179#245']' - +#167#242'['#164#243'X'#161#243'V'#156#243'W'#156#242#129#178#246#193#216#250 - +#246#249#254#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#252#253#255#233#243#253 - +#192#221#250#144#195#247'g'#173#244']'#168#243']'#168#243']'#167#242'^'#166 - +#243'r'#176#245#160#199#248#213#230#252#253#254#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#254#254 - +#255#233#243#253#207#229#251#174#212#249#136#191#246'c'#171#243']'#168#243']' - +#168#243']'#168#243'h'#174#244#133#189#246#169#208#248#207#229#252#245#249 - +#254#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#244#249#254#237#245#254#238#246#254 - +#240#247#254#241#247#254#240#247#254#238#246#254#234#244#253#229#241#253#221 - +#237#253#207#229#251#189#220#250#168#208#249#144#195#247'u'#181#245'c'#171 - +#243']'#168#243']'#168#243'e'#172#244'v'#181#245#141#194#247#172#211#249#204 - +#227#251#233#243#253#250#252#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#223#238#253#189#220#250#181#215#250#172#210#249 - +#162#205#248#152#200#247#143#195#247#134#190#246#128#187#246'{'#184#245'w' - +#182#245'|'#185#245#134#190#246#146#196#247#158#203#248#174#212#249#195#223 - +#251#214#233#252#229#241#253#246#250#254#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 - +#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0 - ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#6'TLabel'#6'Label1'#4'Left'#2#127#3'T' - +'op'#2#24#5'Width'#3#138#0#6'Height'#2#16#9'Alignment'#7#8'taCenter'#8'AutoS' - +'ize'#8#7'Caption'#6#14'TPSQLUpdateSQL'#12'Font.Charset'#7#15'DEFAULT_CHARSE' - +'T'#10'Font.Color'#7#7'clBlack'#11'Font.Height'#2#244#9'Font.Name'#6#5'Arial' - +#10'Font.Style'#11#6'fsBold'#0#10'ParentFont'#8#11'Transparent'#9#0#0#6'TLab' - +'el'#6'Label2'#4'Left'#2#13#3'Top'#3#173#0#5'Width'#3#213#0#6'Height'#2#13#7 - +'Caption'#6'((c) 1999-2007 Pavlo Golub'#0#0#6'TLabel'#12'Vers' - +'ionLabel'#4'Left'#3#130#0#3'Top'#2'2'#5'Width'#3#135#0#6'Height'#2#13#9'Ali' - +'gnment'#7#8'taCenter'#8'AutoSize'#8#7'Caption'#6#2'V.'#11'Transparent'#9#0#0 - +#6'TLabel'#6'Label5'#4'Left'#2#16#3'Top'#2'`'#5'Width'#3#1#1#6'Height'#2')'#9 - +'Alignment'#7#8'taCenter'#8'AutoSize'#8#7'Caption'#6'+Direct Access Componen' - +'ts for PostgreSQL(tm)'#12'Font.Charset'#7#12'ANSI_CHARSET'#10'Font.Color'#7 - +#7'clBlack'#11'Font.Height'#2#240#9'Font.Name'#6#5'Arial'#10'Font.Style'#11#6 - +'fsBold'#0#10'ParentFont'#8#8'WordWrap'#9#0#0#6'TLabel'#8'RegLabel'#4'Left'#2 - +#13#3'Top'#3#152#0#5'Width'#3#4#1#6'Height'#2#13#9'Alignment'#7#8'taCenter'#8 - +'AutoSize'#8#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color'#7#8'clMar' - +'oon'#11'Font.Height'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.Style' - +#11#6'fsBold'#0#10'ParentFont'#8#0#0#6'TLabel'#6'Label3'#4'Left'#2#16#3'Top' - +#3#192#0#5'Width'#2'G'#6'Height'#2#13#6'Cursor'#7#11'crHandPoint'#7'Caption' - +#6#15'Ask for support'#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color' - +#7#6'clBlue'#11'Font.Height'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.' - +'Style'#11#0#10'ParentFont'#8#7'OnClick'#7#17'SpeedButton1Click'#0#0#6'TLabe' - +'l'#6'Label4'#4'Left'#2#16#3'Top'#3#208#0#5'Width'#2'H'#6'Height'#2#13#6'Cur' - +'sor'#7#11'crHandPoint'#7'Caption'#6#13'Check updates'#12'Font.Charset'#7#15 - +'DEFAULT_CHARSET'#10'Font.Color'#7#6'clBlue'#11'Font.Height'#2#245#9'Font.Na' - +'me'#6#13'MS Sans Serif'#10'Font.Style'#11#0#10'ParentFont'#8#7'OnClick'#7#17 - +'SpeedButton2Click'#0#0#6'TLabel'#6'Label6'#4'Left'#2#16#3'Top'#3#224#0#5'Wi' - +'dth'#2'+'#6'Height'#2#13#6'Cursor'#7#11'crHandPoint'#7'Caption'#6#7'Buy Now' - +#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color'#7#6'clBlue'#11'Font.H' - +'eight'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.Style'#11#0#10'Parent' - +'Font'#8#7'OnClick'#7#17'SpeedButton3Click'#0#0#7'TButton'#7'Button1'#4'Left' - +#3#207#0#3'Top'#3#248#0#5'Width'#2'K'#6'Height'#2#25#7'Anchors'#11#7'akRight' - +#0#7'Caption'#6#2'OK'#11'ModalResult'#2#1#8'TabOrder'#2#0#0#0#0 -]); +LazarusResources.Add('TPSQLAboutComp','FORMDATA',[ + 'TPF0'#14'TPSQLAboutComp'#13'PSQLAboutComp'#4'Left'#3#155#1#3'Top'#3#251#0#11 + +'BorderIcons'#11#12'biSystemMenu'#0#11'BorderStyle'#7#8'bsDialog'#7'Caption' + +#6#8'About...'#12'ClientHeight'#3#22#1#11'ClientWidth'#3#31#1#5'Color'#7#9'c' + +'lBtnFace'#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color'#7#12'clWind' + +'owText'#11'Font.Height'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.Styl' + +'e'#11#0#8'Position'#7#14'poScreenCenter'#8'OnCreate'#7#10'FormCreate'#13'Pi' + +'xelsPerInch'#2'`'#0#6'TBevel'#6'Bevel1'#4'Left'#2#8#3'Top'#2#8#5'Width'#3#17 + +#1#6'Height'#3#233#0#5'Shape'#7#7'bsFrame'#0#0#6'TImage'#6'Image1'#4'Left'#2 + +#13#3'Top'#2#14#5'Width'#3#4#1#6'Height'#2';'#8'AutoSize'#9#6'Center'#9#12'P' + +'icture.Data'#10#6#180#0#0#7'TBitmap'#250#179#0#0'BM'#250#179#0#0#0#0#0#0'6' + +#0#0#0'('#0#0#0#4#1#0#0';'#0#0#0#1#0#24#0#0#0#0#0#196#179#0#0#18#11#0#0#18#11 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#253#251#245#239#213#155#242#223#177#255#255#254#255#255#255#187 + +#187#187#187#187#187#255#255#255#255#255#255#255#255#255#187#187#187#187#187 + +#187#255#255#255#255#255#255#255#255#255#187#187#187#187#187#187#255#255#255 + +#238#238#238#187#187#187#204#204#204#255#255#255#255#255#255#255#255#255#238 + +#238#238#136#136#136#136#136#136#136#136#136#238#238#238#255#255#255#255#255 + +#255#204#204#204#187#187#187#238#238#238#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#153#153#153#136#136#136#136#136#136#221#221#221#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#238#238 + +#238#153#153#153#136#136#136#136#136#136#153#153#153#238#238#238#255#255#255 + +#255#255#255#255#255#255#255#255#255#221#221#221#187#187#187#187#187#187#187 + +#187#187#187#187#187#187#187#187#187#187#187#187#187#187#187#187#187#221#221 + +#221#187#187#187#187#187#187#221#221#221#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#204#204#204#187#187#187#187#187#187#238#238#238#255 + +#255#255#187#187#187#187#187#187#204#204#204#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#247#237 + +#213#227#186'^'#238#211#150#253#251#244#255#255#255#255#255#255#0#0#0#0#0#0 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 + +#255#255#0#0#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255#255 + +#255#204#204#204#17#17#17#0#0#0#0#0#0#0#0#0#17#17#17#204#204#204#255#255#255 + +'DDD'#0#0#0#187#187#187#255#255#255#255#255#255#255#255#255#238#238#238'333' + +#0#0#0#0#0#0#0#0#0#0#0#0#153#153#153#255#255#255#255#255#255#255#255#255#255 + +#255#255#153#153#153#17#17#17#0#0#0#0#0#0#0#0#0#0#0#0#17#17#17#153#153#153 + +#255#255#255#255#255#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#153#153#153#0#0#0#0#0#0'fff'#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255'"""'#0#0#0#0#0#0#221#221#221#255#255#255#0#0 + +#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#241#221#177#217#165'.'#240#219#170#255#255#255#255 + +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255 + +#187#187#187#0#0#0'DDD'#255#255#255#255#255#255'333'#0#0#0'"""'#136#136#136 + +#17#17#17#0#0#0'DDD'#255#255#255'DDD'#0#0#0#187#187#187#255#255#255#255#255 + +#255#255#255#255'www'#0#0#0#0#0#0'www333'#0#0#0#17#17#17#221#221#221#255#255 + +#255#255#255#255#187#187#187#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#187#187#187#255#255#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#238#238#238#0#0#0#0#0#0#17#17#17#255#255#255#255#255#255 + +#255#255#255#255#255#255#204#204#204#0#0#0#0#0#0'333'#255#255#255#255#255#255 + +#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#236#210#155#210#148#12#239#218#169#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 + +#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255 + +#255#187#187#187#0#0#0'DDD'#255#255#255#204#204#204#0#0#0#17#17#17#221#221 + +#221#255#255#255#221#221#221#0#0#0#0#0#0#221#221#221'DDD'#0#0#0#187#187#187 + +#255#255#255#255#255#255#255#255#255#17#17#17#0#0#0#136#136#136#255#255#255 + +#238#238#238'"""'#0#0#0#136#136#136#255#255#255#255#255#255'"""'#0#0#0#0#0#0 + +'333'#153#153#153#153#153#153'333'#0#0#0#0#0#0'"""'#255#255#255#255#255#255 + +#136#136#136#0#0#0#0#0#0'fff'#136#136#136#136#136#136#136#136#136#136#136#136 + +#136#136#136#255#255#255'UUU'#0#0#0#0#0#0#204#204#204#255#255#255#255#255#255 + +#255#255#255#136#136#136#0#0#0#0#0#0'www'#255#255#255#255#255#255#0#0#0#0#0#0 + +'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#237#213 + +#161#207#141#0#231#197'|'#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#187#187#187 + +#0#0#0'DDD'#255#255#255#136#136#136#0#0#0'UUU'#255#255#255#255#255#255#255 + +#255#255'wwwfff'#221#221#221'DDD'#0#0#0#187#187#187#255#255#255#255#255#255 + +#204#204#204#0#0#0#17#17#17#255#255#255#255#255#255#255#255#255#136#136#136#0 + +#0#0'UUU'#255#255#255#170#170#170#0#0#0#0#0#0'"""'#238#238#238#255#255#255 + +#255#255#255#238#238#238'"""'#0#0#0#0#0#0#170#170#170#255#255#255#136#136#136 + +#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#204#204#204#255#255#255#255#255#255#0#0#0#0#0#0'DDD'#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#240#220#178#206#143#7#218#169'<'#254 + +#252#248#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255 + +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255 + +#255#255#136#136#136#0#0#0#136#136#136#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255'DDD'#0#0#0#187#187#187#255#255#255#255 + +#255#255#187#187#187#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#187#187 + +#187#0#0#0'DDD'#255#255#255#136#136#136#0#0#0#0#0#0#153#153#153#255#255#255 + +#255#255#255#255#255#255#255#255#255#153#153#153#0#0#0#0#0#0#136#136#136#255 + +#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#221#221#221#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#17#17#17#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0 + ,#255#255#255#255#255#255#255#255#255#255#255#255#247#237#216#209#150#26#206 + +#140#1#243#226#190#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255 + +#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#187 + +#187#187#0#0#0'DDD'#255#255#255#136#136#136#0#0#0#136#136#136#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255'DDD'#0#0#0#187#187 + +#187#255#255#255#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255#255#255 + +#255#255#255#187#187#187#0#0#0'DDD'#255#255#255'DDD'#0#0#0#0#0#0#221#221#221 + +#255#255#255#255#255#255#255#255#255#255#255#255#221#221#221#0#0#0#0#0#0'DDD' + +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255'333'#0#0#0#0#0#0'33' + +'3DDD"""'#0#0#0#0#0#0'fff'#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#17 + +#17#17'DDDDDDDDDwww'#221#221#221#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255 + +#254#252#248#218#171'L'#204#138#0#219#171'D'#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#250#253#250#255#255#255#255#255#255#0#0#0#0#0#0 + +#238#238#238#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 + +#255#255#0#0#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#136#136 + +#136#0#0#0'fff'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255'DDD'#0#0#0#187#187#187#255#255#255#255#255#255#187#187#187#0#0#0 + +'"""'#255#255#255#255#255#255#255#255#255#153#153#153#0#0#0'DDD'#255#255#255 + +'DDD'#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#0#0#0#0#0#0'DDD'#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#136#136#136#0#0#0#0#0#0#153#153#153#255#255#255'fff'#0#0#0#0#0#0#153 + +#153#153#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#17#17#17#204#204#204#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#237#215#170 + +#203#136#0#204#138#0#234#206#147#255#255#255#255#255#255#255#255#255#253#254 + +#253#196#232#201#204#235#209#254#254#254#255#255#255#0#0#0#0#0#0#153#153#153 + +#255#255#255#221#221#221#0#0#0#0#0#0#170#170#170#255#255#255#238#238#238#0#0 + +#0#0#0#0#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#187#187#187#0#0#0'""' + +'"'#238#238#238#255#255#255#221#221#221#0#0#0'333'#255#255#255'DDD'#0#0#0'ww' + +'w'#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#187#187#187#255#255#255 + +#255#255#255'DDD'#0#0#0#136#136#136#255#255#255'DDD'#0#0#0#0#0#0#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0'DDD' + +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#204#204#204#0#0#0#0 + +#0#0'UUU'#255#255#255#17#17#17#0#0#0#0#0#0#238#238#238#255#255#255#255#255 + +#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'"""'#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255 + +#255#255#255#254#253#250#213#163';'#203#136#0#205#141#7#245#230#201#255#255 + +#255#255#255#255#235#247#236#155#215#161#182#226#188#245#251#246#255#255#255 + +#255#255#255#0#0#0#0#0#0#17#17#17#153#153#153'fff'#0#0#0#0#0#0#17#17#17#170 + +#170#170'UUU'#0#0#0'"""'#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255 + +#255#255#17#17#17#0#0#0'UUU'#187#187#187'333'#0#0#0'UUU'#255#255#255'DDD'#0#0 + +#0#17#17#17#153#153#153#136#136#136#255#255#255'UUU'#0#0#0#17#17#17#170#170 + +#170'fff'#0#0#0#0#0#0#204#204#204#255#255#255'DDD'#0#0#0#0#0#0#238#238#238 + +#255#255#255#255#255#255#255#255#255#255#255#255#238#238#238#0#0#0#0#0#0'DDD' + +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255'"""'#0#0 + +#0#17#17#17#204#204#204#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255 + +#255#255#255#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#187#187#187#17#17#17#0 + +#0#0#0#0#0#221#221#221#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255#241#225#192#202#137#4#203#136#0#212#149#26 + +#248#241#224#255#255#255#212#238#214'z'#201'}'#189#228#192#255#255#255#255 + +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#17#17#17#0#0#0#0#0#0#0#0#0'UUU' + +#0#0#0#0#0#0#0#0#0#0#0#0'www'#255#255#255#187#187#187#0#0#0'DDD'#255#255#255 + +#255#255#255#153#153#153#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#187#187#187#255#255 + +#255'DDD'#0#0#0#0#0#0#0#0#0#0#0#0#221#221#221#221#221#221#17#17#17#0#0#0#0#0 + +#0#0#0#0#0#0#0'www'#255#255#255#255#255#255'www'#0#0#0#0#0#0#170#170#170#255 + +#255#255#255#255#255#255#255#255#255#255#255#170#170#170#0#0#0#0#0#0'www'#255 + +#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255'www'#0#0#0#0 + +#0#0'DDD'#0#0#0#0#0#0#136#136#136#255#255#255#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255'www'#0#0#0#0#0#0 + +#187#187#187#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 + +#255#255#255#255#255#227#192'|'#201#135#0#202#136#0#220#151'#'#251#251#246 + +#198#234#203'b'#190'b'#190#228#191#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#136#136#136#136#136#136#204#204#204'UUUDDD'#136#136#136 + +#255#255#255#170#170#170'DDDDDD'#136#136#136#255#255#255#255#255#255#221#221 + +#221#136#136#136#153#153#153#255#255#255#255#255#255#255#255#255#187#187#187 + ,'UUUDDDUUU'#187#187#187#255#255#255#255#255#255#153#153#153#136#136#136#136 + +#136#136'DDDfff'#238#238#238#255#255#255#221#221#221'fffDDDDDD'#153#153#153 + +#255#255#255#255#255#255#255#255#255#153#153#153#0#0#0#0#0#0'DDD'#255#255#255 + +#255#255#255#255#255#255#255#255#255'DDD'#0#0#0#0#0#0#153#153#153#255#255#255 + +#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#187#187#187#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#204#204#204#255#255#255#255#255#255#255#255#255#255 + +#255#255#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255'fff'#0#0#0#0#0 + +#0#187#187#187#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0 + +#255#255#255#255#255#255#216#168'J'#200#135#0#208#135#3#240#212#164#211#232 + +#199'R'#189'Y'#162#217#163#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#238#238#238#17 + +#17#17#0#0#0#0#0#0'fff'#221#221#221#238#238#238'fff'#0#0#0#0#0#0#17#17#17#238 + +#238#238#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#17#17#17#0#0#0#0#0#0#0#0#0'"""'#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#0#0#0#0#0#0'333'#187#187#187#187#187#187#136#136 + +#136#0#0#0#0#0#0#0#0#0#221#221#221#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#0#0#0#0#0#0#255#255#255#255#255#255#208#152''''#203#134#0#229#198 + +#133#230#237#213'['#191']w'#197's'#248#252#248#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#221#221#221#136#136#136#153 + +#153#153#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#136#136#136#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#136#136#136#255#255#255#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255'fff'#0#0#0#0#0#0#0#0#0'fff'#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0'DDD'#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#0#0#0#0#0#0#255#255#255#255#255#255#203#140#15#224#160'>'#241#242#226'i'#198 + +'jT'#185'N'#205#236#206#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#187#187#187#0#0#0'DDD'#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255'fff'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'www'#255#255#255#255#255#255 + +#255#255#255#136#136#136#0#0#0#0#0#0#187#187#187#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#170#170#170#0#0#0#0#0#0#0#0#0#170#170#170#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#17 + +#17#17#204#204#204#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0#255#255#255#255#255#255#214#148'$'#235#227#195#155#209#136 + +'Q'#184'H'#132#200'w'#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#251#253#255#254#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#221#221#221#136#136#136#153#153 + +#153#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#187#187#187'fffDDDDDDfff'#187#187#187 + +#255#255#255#255#255#255#255#255#255#255#255#255#187#187#187#136#136#136#136 + +#136#136#221#221#221#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#136#136#136 + +#136#136#136#136#136#136#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#136#136#136#136#136#136#136#136#136#136#136#136#136#136 + +#136#136#136#136#153#153#153#238#238#238#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#235 + +#208#158#211#229#191'O'#187'HO'#186'L'#171#193'l'#249#233#207#255#255#255#255 + ,#255#255#255#255#255#199#218#250#201#220#251#254#254#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#254#253#251#127#202 + +'pS'#183'A]'#190'W'#183#175'J'#235#172'G'#255#255#255#240#245#255#154#187#247 + +#168#198#248#241#246#254#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#254#254#252#254#252#249#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#0#0#0#0#0#0#255#255#255#255#255#255#222#236#207'T'#183'>S'#183'Aj'#191'^' + +#205#176'H'#236#206#153#229#231#237'l'#159#255#169#200#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#240#220#175#218 + +#168':'#222#177'R'#227#190'p'#232#201#139#237#213#165#241#224#190#247#236#216 + +#251#245#235#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255 + +#255#255#255#255#169#219#154'V'#181'9S'#182'Bj'#194'R'#242#235#207#224#225 + +#234'K'#138#255#186#197#219#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#227#189'h'#207#142#0#206#140#0#205 + +#139#0#204#137#0#203#136#0#203#136#3#202#137#9#218#174'X'#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#137#203'oU'#181 + +'9U'#183'='#188#226#177#220#230#238'D'#127#255#139#158#200#225#179'T'#248#229 + +#193#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#252#248 + +#239#215#160''''#207#142#0#206#140#0#205#139#0#204#137#0#203#136#0#202#135#0 + +#200#133#0#233#206#154#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255'p'#192'PU'#182'7'#164#217#150#225#235#243'J|' + +#255'h'#143#227#206#169'\'#218#146#0#215#156#23#242#222#175#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#236#208#147#208#143#0#207#142#0#206#140 + +#0#205#139#0#204#137#0#203#136#0#202#135#0#214#164'@'#253#251#246#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255'`'#185 + +'?y'#200'W'#232#243#236'e'#138#246'm'#250'z' + +#159#235#217#192#132#216#143#0#209#145#0#211#146#0#213#149#0#214#151#0#227 + +#181'H'#248#235#205#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#227#185'\'#209#144#0#208#143#0#207#142#0#206#140#0 + +#205#139#0#204#137#0#202#136#1#235#210#160#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#184 + +#224#166#202#220#229'Dj'#251'?m'#248#132#182#206#226#233#199#222#159'*'#210 + +#144#0#211#146#0#213#149#0#214#151#0#216#152#0#220#162#20#237#206#130#252#246 + +#231#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#235#206#139#211#146#0#209 + +#144#0#212#142#0#213#140#0#210#139#0#207#138#0#204#137#0#233#196#131#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255#252#253#252'x'#145#250'@k'#240'Mw'#247't' + +#181#162#155#215#144#233#226#188#218#145#7#211#147#0#213#149#0#214#151#0#216 + +#152#0#217#154#0#219#156#0#225#172'('#240#212#141#252#245#227#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#239#213 + +#153#213#150#5#211#146#0#213#148#10#227#203#134#224#206#145#222#190'o'#220 + +#174'P'#239#203#148#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#217 + +#226#244'Ej'#243'@k'#239']'#133#240'}'#189#147'P'#192'R'#207#228#190#242#211 + +#157#214#146#2#213#149#0#214#151#0#216#152#0#217#154#0#219#156#0#220#157#0 + +#221#159#0#227#170#24#239#208'~'#254#253#248#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#254#236#207#135#215#152#6#212#148#0#212#145#0#223#167'9'#224#234 + +#206#146#211#148#169#213#153#189#218#165#210#226#185#210#234#201#223#242#221 + +#238#248#235#246#251#243#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255#164#180#250'@h'#236'@k'#240'd'#139#237#130#194 + +#147'X'#188'K]'#194'k'#234#239#218#244#216#167#218#153#12#214#151#0#216#152#0 + +#217#154#0#219#156#0#220#157#0#221#159#0#231#183'='#246#228#181#255#255#254 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#252#247#234#231#192'c'#215#152#0#214#150#0#212#148#0 + +#215#145#0#232#207#144#178#217#165'O'#188'ZQ'#186'QP'#185'NM'#185'HR'#183'A[' + +#184'Af'#188'H'#150#208'{'#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255'{'#147#242'@h'#237'@k'#239'c'#137#241#139 + +#197#159'X'#188'JV'#186'Xj'#201'~'#240#244#229#248#230#196#225#168'.'#216#152 + +#0#217#154#0#219#156#0#224#167#27#240#214#145#254#253#249#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#245#229#188#225#175'6'#216#152#0#215#152#0#214#150#0#212#148 + +#0#222#152#20#239#236#210'v'#203#129'W'#185'TV'#184'NV'#183'JV'#182'DU'#182 + +'?U'#181':U'#180'2'#180#222#161#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#0#0#0#0#0#0#255#255#255#255#255#255'_|'#239'@h'#237'@l'#237'U}'#247#155#200 + +#187'X'#189'HW'#186'XW'#187']e'#199'~'#230#237#213#252#248#239#240#195'n'#223 + +#161#20#233#193'`'#250#242#220#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#249#239 + +#214#234#196'e'#219#159#8#217#154#0#216#152#0#215#152#0#213#150#0#215#149#4 + +#240#205#143#207#229#194'Q'#188']W'#185'TV'#184'NV'#183'JV'#182'DU'#182'?U' + +#181'9z'#196'^'#248#252#246#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255'Sr'#238'@h'#237'@m'#237'Bn'#248#165#199#216 + +'k'#197']W'#186'XX'#187'\Y'#188'b_'#196'v'#198#229#196#255#255#255#251#245 + +#231#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#249#238#208#236#201'o'#224#167#23 + +#220#157#0#218#156#0#217#154#0#216#152#0#216#154#5#227#180'K'#246#223#182#248 + +#249#242'n'#200'zW'#186'YW'#185'TV'#184'NV'#183'JV'#182'DU'#182'?\'#184'B' + ,#211#236#202#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255'Wv'#238'@h'#237'@l'#237'Ao'#242#138#171#237#160 + +#212#166'X'#188'QX'#187']Y'#188'b['#190'gY'#192'o'#142#217#170#239#248#238 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#253#250#240#246#226#175#234#193'V'#225#167#16#222#160#0#221#158#0#220#157#0 + +#218#156#0#221#164#25#232#195'k'#247#232#198#255#255#255#255#255#255#150#218 + +#166'X'#187']W'#186'XW'#185'TV'#184'NV'#183'JV'#182'DU'#182'?'#184#225#173 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255'k'#134#240'@h'#237'@l'#237'Ap'#238'S}'#249#203 + +#225#228'n'#199'fX'#187'\Y'#188'b['#190'g\'#191'l^'#192'po'#203#138#186#230 + +#202#247#252#249#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#254#250#242#248#232#190#240#209'~'#232#182 + +'3'#225#164#0#224#162#0#223#161#0#222#160#0#223#163#14#230#185'H'#239#212#144 + +#250#241#220#255#255#255#255#255#255#255#255#255#187#231#198'Y'#189'aX'#187 + +'\W'#187'UX'#187'NV'#185'JV'#184'IV'#182'C'#162#217#144#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 + +#255#255#255#255#255#140#161#244'@h'#237'@l'#237'Ap'#237'Bs'#242#152#182#242 + +#199#231#203'['#191'WY'#188'c['#190'g\'#191'l^'#192'p_'#193'ta'#194'y'#133 + +#209#154#198#233#208#245#251#247#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#254#253#252#245#227#248#233#194 + +#243#216#147#237#197']'#230#176'#'#226#164#0#226#164#0#226#164#0#226#166#7 + +#229#178'+'#234#194'Z'#242#217#153#249#239#212#254#253#250#255#255#255#255 + +#255#255#255#255#255#255#255#255#197#234#208'b'#192'mY'#188'`]'#190'`'#161 + +#215#167#165#215#172#146#208#144#132#203'w'#174#222#157#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255#190#202#249'@h'#237'@l'#237'Ap'#237'Bu'#238 + +'Fx'#247#198#220#233#185#228#180'Z'#189'^Z'#190'h\'#191'l^'#192'p_'#193'ta' + +#194'yc'#196'~e'#197#130'{'#205#149#183#228#198#252#254#253#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#252#247#232#250#240#213#251#242#218 + +#251#242#219#251#242#218#250#240#214#250#238#208#249#235#200#247#230#186#245 + +#223#166#242#214#141#239#204'o'#235#192'N'#231#178'('#228#171#21#227#166#5 + +#226#164#0#227#167#8#230#175#31#234#189'G'#239#204'q'#244#221#160#249#238#207 + +#253#248#237#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#185#229#196'a'#193'oZ'#189'eY'#188'_{'#203'w'#212#230 + +#238#149#182#244#165#195#234#178#205#231#202#220#237#216#225#251#230#235#254 + +#240#243#254#247#248#254#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0 + +#0#0#255#255#255#255#255#255#240#243#254'X{'#239'@l'#237'Ap'#237'Bu'#238'Cx' + +#238'W'#133#253#229#240#244#190#231#187'^'#192'f\'#191'm^'#192'p_'#193'ta' + +#194'yc'#196'~e'#197#130#134#209#158#207#237#217#254#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#250#240#212#245#222#163#243#218#150 + +#241#212#135#240#206'w'#238#201'i'#236#197'\'#235#192'N'#234#190'H'#234#190 + +'J'#235#192'N'#236#194'U'#237#197']'#239#204'p'#242#213#137#245#223#165#248 + +#232#190#250#240#214#253#250#241#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#248#252#249#162#219#176'\'#191'm['#190'iZ'#189'eZ'#189'\'#177#222#183#165 + +#196#238'Dz'#250'Cx'#246'Bt'#247'Ao'#248'Bo'#241'Ox'#237'_'#128#240#140#161 + +#244#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 + +#255#255#255#255#255#155#176#246'@l'#237'Ap'#237'Bu'#238'Cx'#238'D|'#240'l' + +#151#251#241#247#249#205#237#205'q'#201'x^'#192'p_'#193'ta'#194'yo'#200#136 + +#178#226#193#247#252#248#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#254#252 + +#254#253#248#254#252#246#254#252#246#254#252#247#254#253#249#255#254#251#255 + +#254#253#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#223#243#228#140 + +#211#155'^'#191'q\'#191'm['#190'iZ'#189'fb'#195'_'#221#238#231't'#159#249'E' + +#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237'@l'#237'@h'#237#161#179#246#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 + +#255#255#237#241#253']'#130#240'Ap'#237'Bu'#238'Cx'#238'D|'#239'F'#127#240'g' + +#149#253#224#238#243#241#249#243#156#218#162'i'#197'|'#150#214#165#229#245 + +#233#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#237#248#241#170#223#184'k'#198#128 + +'^'#193's^'#191'p\'#191'm['#190'i['#189'd'#177#224#178#201#223#236'G'#127#246 + +'E'#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237'@l'#237'b'#131#240#245#247#254 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255 + +#255#255#255#255#255#255#255#194#209#249'Dr'#237'Bu'#238'Cx'#238'D|'#239'F' + +#127#239'G'#132#240'Z'#144#249#196#218#250#255#255#255#231#247#232#252#254 + +#252#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#233#247#237#176#225#192'x'#204#144'b'#195'|a' + +#194'x^'#193's^'#191'p\'#191'n'#132#207#140#201#235#198#245#250#249'k'#154 + +#252'F'#129#240'E'#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237'Fq'#238#200#212 + ,#250#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#155#180#246'Bu'#238 + +'Cx'#238'D|'#239'F'#127#239'G'#132#239'J'#135#240'L'#138#245#153#191#246#245 + +#249#253#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#252#254#253#220#242#228#167#222#185'u'#203#144'e'#197#131'd'#196#128 + +'b'#195'|a'#194'xg'#197'{'#148#213#160#207#236#211#250#253#250#255#255#255 + +#158#192#245'H'#132#242'F'#130#239'E'#127#239'D{'#239'Cw'#238'Bs'#238'Ao'#237 + +#162#184#246#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#250#251#255#135#167#244'Cx'#238'D|'#239'F'#127#239'G'#132#239'J'#135#240'K' + +#139#240'N'#143#243'l'#162#248#194#218#253#252#253#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#251 + +#253#252#228#245#233#190#231#204#147#214#170'o'#201#141'j'#199#136'g'#198#133 + +'e'#197#131'h'#198#131#132#208#152#175#224#187#223#243#227#255#255#255#255 + +#255#255#255#255#255#195#215#255'N'#138#244'H'#133#239'F'#130#239'E'#127#239 + +'D{'#239'Cw'#238'Bs'#238#138#166#244#251#252#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#248#250#254#136#170#244'D|' + +#239'F'#127#239'G'#132#239'J'#135#240'K'#139#241'N'#144#241'O'#146#240'R'#151 + +#242#130#181#245#202#224#251#250#252#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#246#252#248#226 + +#244#232#202#235#214#171#223#189#137#211#162'm'#200#140'm'#200#140'm'#200#140 + +'l'#200#139'}'#206#151#149#215#170#185#229#199#221#242#227#251#253#251#255 + +#255#255#255#255#255#255#255#255#255#255#255#204#223#253'X'#149#241'J'#136 + +#240'H'#133#240'F'#130#239'E'#127#239'D{'#239'Cw'#238#140#170#245#248#250#254 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#251#252#255#157#186#247'H'#129#239'G'#132#239'J'#135 + +#240'K'#139#241'N'#144#241'O'#146#240'R'#151#242'T'#154#242'W'#157#243'z'#180 + +#245#184#217#250#252#254#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#244#251#246#236#248#240#238#249#241#238#249#241#237#248#241#236#248 + +#240#234#247#239#232#246#237#226#244#232#216#240#224#202#235#214#185#229#200 + +#167#222#186#143#213#167'{'#205#151'q'#202#143'm'#200#140'm'#200#140'x'#204 + +#149#138#211#163#162#220#182#190#230#204#218#241#225#239#249#242#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#194 + +#216#252'Y'#151#242'K'#140#240'J'#136#240'H'#133#240'F'#130#239'E'#127#239'H' + +'~'#239#158#185#246#251#252#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#188#209#249'['#146#241'J'#135#240'K'#139#241'N'#144#241'O' + +#146#240'R'#151#242'T'#154#242'W'#157#243'n'#174#245#193#221#250#252#254#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#228#245#234#198#233 + +#210#190#230#204#181#227#196#172#224#190#165#221#184#158#218#178#150#216#172 + +#144#213#168#140#212#165#144#213#168#149#215#172#155#217#176#163#220#182#175 + +#225#192#192#231#205#209#238#219#223#243#230#238#249#241#253#254#254#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#252#253#255#170#203#250'U'#149#240'N'#144#241'K'#140 + +#240'J'#136#240'H'#133#240'F'#130#239'X'#140#241#193#212#250#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#223#234#252#133#174#245'K'#139#241'N'#144#241'O'#146#240'R'#151#242'\' + +#159#243#163#201#248#239#246#254#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#252#254#252#249 + +#253#250#248#252#249#246#252#248#246#252#248#246#252#248#248#252#250#250#253 + +#251#252#254#252#254#255#254#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#229#240 + +#253#140#187#246'R'#150#242'P'#146#240'N'#144#241'K'#140#240'J'#136#240'H' + +#133#240#128#169#244#220#231#252#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#254#254#255#194#216#250's'#167#244'T'#149#240#129#179#246#214#231#252#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#243#248#254#176#211#249'f'#165#244'T'#154 + +#242'R'#150#242'P'#146#240'N'#144#241'K'#140#240'o'#160#243#193#213#250#252 + +#253#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#243#248#254#207#226#251#248#251#254#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#240 + +#247#254#184#216#249'y'#180#245'X'#161#243'V'#156#243'T'#154#242'R'#150#242 + +'P'#146#240'q'#166#244#181#208#249#244#248#254#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#226#239#253#169#209#249'r'#179#245']' + +#167#242'['#164#243'X'#161#243'V'#156#243'W'#156#242#129#178#246#193#216#250 + +#246#249#254#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#252#253#255#233#243#253 + +#192#221#250#144#195#247'g'#173#244']'#168#243']'#168#243']'#167#242'^'#166 + +#243'r'#176#245#160#199#248#213#230#252#253#254#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#254#254 + +#255#233#243#253#207#229#251#174#212#249#136#191#246'c'#171#243']'#168#243']' + +#168#243']'#168#243'h'#174#244#133#189#246#169#208#248#207#229#252#245#249 + +#254#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#244#249#254#237#245#254#238#246#254 + +#240#247#254#241#247#254#240#247#254#238#246#254#234#244#253#229#241#253#221 + +#237#253#207#229#251#189#220#250#168#208#249#144#195#247'u'#181#245'c'#171 + +#243']'#168#243']'#168#243'e'#172#244'v'#181#245#141#194#247#172#211#249#204 + +#227#251#233#243#253#250#252#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#223#238#253#189#220#250#181#215#250#172#210#249 + +#162#205#248#152#200#247#143#195#247#134#190#246#128#187#246'{'#184#245'w' + +#182#245'|'#185#245#134#190#246#146#196#247#158#203#248#174#212#249#195#223 + +#251#214#233#252#229#241#253#246#250#254#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#0#0#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + ,#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0 + +#0#0#0#0#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255 + +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#0#0#0#0#0#0 + ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 + +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#6'TLabel'#6'Label1'#4'Left'#2#127#3'T' + +'op'#2#24#5'Width'#3#138#0#6'Height'#2#16#9'Alignment'#7#8'taCenter'#8'AutoS' + +'ize'#8#7'Caption'#6#14'TPSQLUpdateSQL'#12'Font.Charset'#7#15'DEFAULT_CHARSE' + +'T'#10'Font.Color'#7#7'clBlack'#11'Font.Height'#2#244#9'Font.Name'#6#5'Arial' + +#10'Font.Style'#11#6'fsBold'#0#10'ParentFont'#8#11'Transparent'#9#0#0#6'TLab' + +'el'#6'Label2'#4'Left'#2#13#3'Top'#3#173#0#5'Width'#3#213#0#6'Height'#2#13#7 + +'Caption'#6'((c) 1999-2007 Pavlo Golub'#0#0#6'TLabel'#12'Vers' + +'ionLabel'#4'Left'#3#130#0#3'Top'#2'2'#5'Width'#3#135#0#6'Height'#2#13#9'Ali' + +'gnment'#7#8'taCenter'#8'AutoSize'#8#7'Caption'#6#2'V.'#11'Transparent'#9#0#0 + +#6'TLabel'#6'Label5'#4'Left'#2#16#3'Top'#2'`'#5'Width'#3#1#1#6'Height'#2')'#9 + +'Alignment'#7#8'taCenter'#8'AutoSize'#8#7'Caption'#6'+Direct Access Componen' + +'ts for PostgreSQL(tm)'#12'Font.Charset'#7#12'ANSI_CHARSET'#10'Font.Color'#7 + +#7'clBlack'#11'Font.Height'#2#240#9'Font.Name'#6#5'Arial'#10'Font.Style'#11#6 + +'fsBold'#0#10'ParentFont'#8#8'WordWrap'#9#0#0#6'TLabel'#8'RegLabel'#4'Left'#2 + +#13#3'Top'#3#152#0#5'Width'#3#4#1#6'Height'#2#13#9'Alignment'#7#8'taCenter'#8 + +'AutoSize'#8#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color'#7#8'clMar' + +'oon'#11'Font.Height'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.Style' + +#11#6'fsBold'#0#10'ParentFont'#8#0#0#6'TLabel'#6'Label3'#4'Left'#2#16#3'Top' + +#3#192#0#5'Width'#2'G'#6'Height'#2#13#6'Cursor'#7#11'crHandPoint'#7'Caption' + +#6#15'Ask for support'#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color' + +#7#6'clBlue'#11'Font.Height'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.' + +'Style'#11#0#10'ParentFont'#8#7'OnClick'#7#17'SpeedButton1Click'#0#0#6'TLabe' + +'l'#6'Label4'#4'Left'#2#16#3'Top'#3#208#0#5'Width'#2'H'#6'Height'#2#13#6'Cur' + +'sor'#7#11'crHandPoint'#7'Caption'#6#13'Check updates'#12'Font.Charset'#7#15 + +'DEFAULT_CHARSET'#10'Font.Color'#7#6'clBlue'#11'Font.Height'#2#245#9'Font.Na' + +'me'#6#13'MS Sans Serif'#10'Font.Style'#11#0#10'ParentFont'#8#7'OnClick'#7#17 + +'SpeedButton2Click'#0#0#6'TLabel'#6'Label6'#4'Left'#2#16#3'Top'#3#224#0#5'Wi' + +'dth'#2'+'#6'Height'#2#13#6'Cursor'#7#11'crHandPoint'#7'Caption'#6#7'Buy Now' + +#12'Font.Charset'#7#15'DEFAULT_CHARSET'#10'Font.Color'#7#6'clBlue'#11'Font.H' + +'eight'#2#245#9'Font.Name'#6#13'MS Sans Serif'#10'Font.Style'#11#0#10'Parent' + +'Font'#8#7'OnClick'#7#17'SpeedButton3Click'#0#0#7'TButton'#7'Button1'#4'Left' + +#3#207#0#3'Top'#3#248#0#5'Width'#2'K'#6'Height'#2#25#7'Anchors'#11#7'akRight' + +#0#7'Caption'#6#2'OK'#11'ModalResult'#2#1#8'TabOrder'#2#0#0#0#0 +]); diff --git a/psqlAboutFrm.pas b/Source/psqlAboutFrm.pas similarity index 95% rename from psqlAboutFrm.pas rename to Source/psqlAboutFrm.pas index e5f7434..f1fad39 100644 --- a/psqlAboutFrm.pas +++ b/Source/psqlAboutFrm.pas @@ -1,98 +1,98 @@ -{$I pSQLDAC.inc} -unit psqlAboutFrm; - -{SVN revision: $Id$} - -interface - -uses - {$IFDEF FPC}LCLIntf, LResources, {$ENDIF}Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, - StdCtrls, ExtCtrls, Buttons; - -type - TPSQLAboutComp = class(TForm) - Label1: TLabel; - Label2: TLabel; - Button1: TButton; - VersionLabel: TLabel; - Bevel1: TBevel; - Label5: TLabel; - Image1: TImage; - RegLabel: TLabel; - Label3: TLabel; - Label4: TLabel; - Label6: TLabel; - procedure FormCreate(Sender: TObject); - procedure SpeedButton1Click(Sender: TObject); - procedure SpeedButton2Click(Sender: TObject); - procedure SpeedButton3Click(Sender: TObject); - private - { Private declarations } - FVersion : string; - FCompName : String; - FRegister : String; - public - { Public declarations } - property Version: string read FVersion write FVersion; - property CompName :string read FCompName write FCompname; - property RegVersion :string read FRegister write FRegister; - end; - -var - PSQLAboutComp: TPSQLAboutComp; - -procedure Dac4PSQLShowAbout(aComponentName : string); - -implementation - -{$IFNDEF FPC} - {$R *.DFM} -{$ENDIF} - -uses ShellAPI, PSQLDbTables; - -procedure Dac4PSQLShowAbout(aComponentName : string); -begin - with TPSQLAboutComp.Create(Application) do - try - Caption := 'Thank you for trying PostgresDAC'; - VersionLabel.Caption := 'v.' + PSQLDBTables.VERSION; - Label1.Caption := aComponentName; - RegLabel.Caption := 'PostgreSQL License'; - ShowModal(); - finally - Free(); - end; -end; - -procedure TPSQLAboutComp.FormCreate(Sender: TObject); -begin - FVersion := ''; - FCompName := ''; - FRegister:=''; - Label2.Caption := '(c) Pavlo Golub'; -end; - -procedure TPSQLAboutComp.SpeedButton1Click(Sender: TObject); -begin - {Send e-mail} - ShellExecute(0,'Open','https://github.com/pashagolub/postgresdac/issues',nil,nil,SW_SHOW); -end; - -procedure TPSQLAboutComp.SpeedButton2Click(Sender: TObject); -begin - {Go to web} - ShellExecute(0,'Open','https://github.com/pashagolub/postgresdac/',nil,nil,SW_SHOW); -end; - -procedure TPSQLAboutComp.SpeedButton3Click(Sender: TObject); -begin - ShellExecute(0,'Open','https://github.com/sponsors/pashagolub/',nil,nil,SW_SHOW); -end; - -initialization -{$IFDEF FPC} - {$i psqlAboutFrm.lrs} -{$ENDIF} - -end. +{$I pSQLDAC.inc} +unit psqlAboutFrm; + +{SVN revision: $Id$} + +interface + +uses + {$IFDEF FPC}LCLIntf, LResources, {$ENDIF}Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, ExtCtrls, Buttons; + +type + TPSQLAboutComp = class(TForm) + Label1: TLabel; + Label2: TLabel; + Button1: TButton; + VersionLabel: TLabel; + Bevel1: TBevel; + Label5: TLabel; + Image1: TImage; + RegLabel: TLabel; + Label3: TLabel; + Label4: TLabel; + Label6: TLabel; + procedure FormCreate(Sender: TObject); + procedure SpeedButton1Click(Sender: TObject); + procedure SpeedButton2Click(Sender: TObject); + procedure SpeedButton3Click(Sender: TObject); + private + { Private declarations } + FVersion : string; + FCompName : String; + FRegister : String; + public + { Public declarations } + property Version: string read FVersion write FVersion; + property CompName :string read FCompName write FCompname; + property RegVersion :string read FRegister write FRegister; + end; + +var + PSQLAboutComp: TPSQLAboutComp; + +procedure Dac4PSQLShowAbout(aComponentName : string); + +implementation + +{$IFNDEF FPC} + {$R *.DFM} +{$ENDIF} + +uses ShellAPI, PSQLDbTables; + +procedure Dac4PSQLShowAbout(aComponentName : string); +begin + with TPSQLAboutComp.Create(Application) do + try + Caption := 'Thank you for trying PostgresDAC'; + VersionLabel.Caption := 'v.' + PSQLDBTables.VERSION; + Label1.Caption := aComponentName; + RegLabel.Caption := 'PostgreSQL License'; + ShowModal(); + finally + Free(); + end; +end; + +procedure TPSQLAboutComp.FormCreate(Sender: TObject); +begin + FVersion := ''; + FCompName := ''; + FRegister:=''; + Label2.Caption := '(c) Pavlo Golub'; +end; + +procedure TPSQLAboutComp.SpeedButton1Click(Sender: TObject); +begin + {Send e-mail} + ShellExecute(0,'Open','https://github.com/pashagolub/postgresdac/issues',nil,nil,SW_SHOW); +end; + +procedure TPSQLAboutComp.SpeedButton2Click(Sender: TObject); +begin + {Go to web} + ShellExecute(0,'Open','https://github.com/pashagolub/postgresdac/',nil,nil,SW_SHOW); +end; + +procedure TPSQLAboutComp.SpeedButton3Click(Sender: TObject); +begin + ShellExecute(0,'Open','https://github.com/sponsors/pashagolub/',nil,nil,SW_SHOW); +end; + +initialization +{$IFDEF FPC} + {$i psqlAboutFrm.lrs} +{$ENDIF} + +end.