{ $Id: PDO.pas 32 2007-01-22 01:35:20Z jmarino $ 

############################################################################################

	Pascal Data Objects Library
	Copyright © 2006 John Marino, http://www.synsport.com
	Project site: http://pdo.sourceforge.net

############################################################################################

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

############################################################################################}


unit PDO;

{$I directives.inc}

interface

uses
    Classes, sysUtils, PDOSQL, PDOSQLDelete, PDOSQLUpdate,
    PDOInterfaces, PDOLogging, PDOClasses, PDOStatement, PDOMessages,PDOSysUtils

{$IFDEF ENABLE_MYSQL}
    , PDOConnectMySQL
    , PDOMySQLDriver
    {$IFDEF MYSQL40DRIVER}
    , PDODriverMySQL40
    {$ENDIF}
    {$IFDEF MYSQL41DRIVER}
    , PDODriverMySQL41
    {$ENDIF}
    {$IFDEF MYSQL50DRIVER}
    , PDODriverMySQL50
    {$ENDIF}
{$ENDIF}
;

type



    TPDO = class
    private
        pre_connect_compression: Boolean;
        pre_connect_multiquery:  Boolean;
        Connection:              IZConnection;
        Listener:                TZAbstractLoggingListener;
        active:                  Boolean;
        numProtocols:            byte;
        protocols:               array of IPDOProtocol;
        protocolIndex:           shortInt;
        subProtocols:            array of byte;
        {$IFDEF ENABLE_MYSQL}
        protoMySQL:              array of TZMySQLBaseDriver;
        {$ENDIF}


        function assemble_select (select: PDO_selectRecord): AnsiString;
        procedure add_listener (ListenerHandle: TZAbstractLoggingListener);
        procedure clear_listener;
        procedure log_something (const Category: TZLoggingCategory; const msg: ansistring;
            const error_code: integer = 0; const error_msg: ansistring = '');
        procedure establish_available_protocols;
        function select_protocol (const dsn: ansistring): boolean;
        function initialize_parameters (const dsn, user_id, password: ansistring;
            const options: longword; info: TStringList): boolean;
        function establish_connection (info: TStringList): IZConnection;
        function protocol_defined: boolean;

    public
        procedure beginTransaction;
        procedure commit;
        procedure rollBack;
        function execute_now (sql: AnsiString): Int64;
        function lastInsertId: Int64;
        function errorCode: AnsiString;
        function errorInfo: PDO_errorInfoRecord;
        function getAttribute  (Attribute: TPDO_Attributes; var TextInformation: AnsiString): TPDO_Characteristics;
        function getAvailableDrivers: TStringList;
        procedure setAttribute (Attribute: TPDO_Attributes; AttributeValue: TPDO_Characteristics);
        procedure setMaxBLOBSize (BLOBSize: Int64);

        function query_as_is  (sql: AnsiString): IPDOStatement;
        function query_select (sql: PDO_selectRecord): IPDOStatement;
        function prepare_as_is (sql: AnsiString): IPDOStatement;
        function prepare_select (sql: PDO_selectRecord): IPDOStatement;

        function connect (dsn, user_id, password: AnsiString): Boolean; overload;
        function connect (dsn, user_id, password: AnsiString; CustomListener: TZAbstractLoggingListener): Boolean; overload;
        procedure disconnect;
        function build_flags: longint;
        constructor create;

        function query_delete (const DeletionMethod: TDeletionMethod;
            const tablename: Ansistring; const condition: Ansistring = '';
            const limit: longword = 0; const order: Ansistring = ''): Int64;

        function query_delete_multitable (const tables: Ansistring; const table_joins: Ansistring;
            const condition: ansistring = ''): int64;

        function query_update (const tablename: Ansistring;
            const set_expressions: Ansistring; const condition: Ansistring = '';
            const order: Ansistring = ''; const limit: longword = 0): int64;

        function query_update_multitable (const table_joins: Ansistring;
            const set_expressions: Ansistring; const condition: ansistring = ''): int64;
    end;

implementation

constructor TPDO.create;
begin
    self.pre_connect_compression := true;
    self.pre_connect_multiquery  := false;
    self.active                  := false;
    self.clear_listener;
    self.establish_available_protocols;
end;

function TPDO.build_flags: longint;
begin
    Result := 0;
    if self.pre_connect_compression then Result := Result + 1;
    if self.pre_connect_multiquery  then Result := Result + 2;
end;

function TPDO.connect (dsn, user_id, password: AnsiString; CustomListener: TZAbstractLoggingListener): Boolean;
Begin
    self.add_listener(CustomListener);

    Result := self.connect(dsn, user_id, password);
end;

function TPDO.connect (dsn, user_id, password: AnsiString): Boolean;
var
    parameters: TStringList;
Begin
    self.disconnect;

    Result := false;
    if (self.select_protocol(dsn)) then
        begin
            parameters := TStringList.Create;
            if (self.initialize_parameters(dsn, user_id, password, self.build_flags, parameters)) then
            begin
                self.protocols[self.protocolIndex].preconnect(parameters, self.Listener);
                self.Connection := self.establish_connection(parameters);
                self.active := not self.Connection.IsClosed;
                Result := self.active;
            end;
            parameters.Free;
        end
    else
        begin
            self.log_something(lcConnect,dsn,0, SDriverWasNotFound);
        end;
end;


procedure TPDO.disconnect;
begin
    if self.active then
        self.Connection.Close;

    self.active := false;
end;


procedure TPDO.BeginTransaction;
Begin
    self.Connection.SetAutoCommit(False);
    self.Connection.SetTransactionIsolation(tiReadCommitted);
end;

procedure TPDO.commit;
Begin
    self.Connection.Commit;
    self.Connection.SetTransactionIsolation(tiNone);
    self.Connection.SetAutoCommit(True);
end;

procedure TPDO.rollBack;
Begin
    self.Connection.Rollback;
    self.Connection.SetTransactionIsolation(tiNone);
    self.Connection.SetAutoCommit(True);
end;

function TPDO.execute_now (sql: AnsiString): Int64;
Begin
    if self.active then
        Result := self.Connection.execute_without_result(sql)
    else
      begin
        Result := 0;
        self.log_something(lcExecute, sql, 0, SQueryWithOutConnect);
      end;
end;

function TPDO.getAvailableDrivers: TStringList;
var
    loop: byte;
Begin
    Result := TStringList.Create;
    for loop := 0 to self.numProtocols - 1 do
        Result.Add( self.protocols[loop].GetProtocol );
    Result.Sorted := true;
end;

function TPDO.lastInsertId: Int64;
Begin
    Result := self.Connection.LastInsertID;
end;

function TPDO.errorCode: AnsiString;
Begin
    Result := self.Connection.SQLState;
end;

function TPDO.errorInfo: PDO_errorInfoRecord;
Begin
    Result.SQLState   := self.Connection.SQLState;
    Result.Error_code := self.Connection.LastErrorCode;
    Result.Error_msg  := self.Connection.LastErrorMsg;
end;

function TPDO.getAttribute (Attribute: TPDO_Attributes; var TextInformation: AnsiString): TPDO_Characteristics;
var
    mode: Boolean;
    bytemode: Byte;
Begin
    TextInformation := '';
    case Attribute of
        AUTOCOMMIT:
            Begin
                mode := self.Connection.GetAutoCommit;
                if (mode) then
                    Result := AUTOCOMMIT_ON
                else
                    Result := AUTOCOMMIT_OFF;
            end;
        COMPRESSION:
             Begin
                mode := self.pre_connect_compression;
                if (mode) then
                    Result := COMPRESSION_ON
                else
                    Result := COMPRESSION_OFF;
            end;
        MULTIQUERY:
            begin
                mode := self.pre_connect_multiquery;
                if (mode) then
                    Result := MULTIQUERY_ON
                else
                    Result := MULTIQUERY_OFF;
            end;
        ERRMODE:
            Begin
                bytemode := self.Connection.SilentMode;
                if (bytemode = ERRORLEVEL_SILENT) then
                    Result := ERRMODE_SILENT
                else
                    if (bytemode = ERRORLEVEL_WARNING) then
                        Result := ERRMODE_WARNING
                    else
                        Result := ERRMODE_EXCEPTION;
            end;
        COLUMN_CASE:
            Begin
                bytemode := self.Connection.CaseMode;
                if (bytemode = CASEMODE_LOWER) then
                    Result := CASE_LOWER
                else
                    if (bytemode = CASEMODE_NATURAL) then
                        Result := CASE_NATURAL
                    else
                        Result := CASE_UPPER;
            end;
        ORACLE_NULLS:
            Begin
                bytemode := self.Connection.OracleNulls;
                if (bytemode = ORACLENULL_NATURAL) then
                    Result := NULL_NATURAL
                else
                    if (bytemode = ORACLENULL_EMPTY_STRING) then
                        Result := NULL_EMPTY_STRING
                    else
                        Result := NULL_TO_STRING;
            end;
         PREFETCH:
            Begin
                mode := self.Connection.BufferQuery;
                if (mode = QUERY_FREFETCH_ON) then
                    Result := PREFETCH_ON
                else
                    Result := PREFETCH_OFF;
            End;
         CONNECTION_STATUS:
            Begin
                mode := self.active;
                if (mode = CONNECTION_ON) then
                    Result := CONNECTED
                else
                    Result := UNCONNECTED;
            End;
         DRIVER_NAME:
            Begin
                Result := TEXTINFO;
                TextInformation := self.Connection.GetDescription;
            End;
         CLIENT_INFO:
            Begin
                Result := TEXTINFO;
                TextInformation := self.Connection.ClientInfo;
            End;
         CLIENT_VERSION:
            Begin
                Result := TEXTINFO;
                TextInformation := self.Connection.ClientVersion;
            End;
         SERVER_INFO:
            Begin
                Result := TEXTINFO;
                TextInformation := self.Connection.ServerInfo;
            End;
         SERVER_VERSION:
            Begin
                Result := TEXTINFO;
                TextInformation := self.Connection.ServerVersion;
            End;
         MAX_BLOB_SIZE:
            Begin
                Result := TEXTINFO;
                TextInformation := IntToStr(self.Connection.MaxBLOBSize);
            End;
    else
        Result := XUNIMPLEMENTED;
    end;
end;

procedure TPDO.setAttribute (Attribute: TPDO_Attributes; AttributeValue: TPDO_Characteristics);
var
    mode: Boolean;
    bytemode: Byte;
Begin
    case Attribute of
        AUTOCOMMIT:
            Begin
                if (AttributeValue = AUTOCOMMIT_ON) then
                    mode := True
                else
                    mode := False;
                self.Connection.SetAutoCommit(mode);
            END;
        ERRMODE:
            Begin
                if (AttributeValue = ERRMODE_SILENT) then
                    bytemode := ERRORLEVEL_SILENT
                else
                    if (AttributeValue = ERRMODE_WARNING) then
                        bytemode := ERRORLEVEL_WARNING
                    else
                        bytemode := ERRORLEVEL_RAISE_EXCEPTION;
                self.Connection.SilentMode := bytemode;
            END;
        COLUMN_CASE:
            Begin
                if (AttributeValue = CASE_LOWER) then
                    bytemode := CASEMODE_LOWER
                else
                    if (AttributeValue = CASE_NATURAL) then
                        bytemode := CASEMODE_NATURAL
                    else
                        bytemode := CASEMODE_UPPER;
                self.Connection.CaseMode := bytemode;
            END;
        ORACLE_NULLS:
            Begin
                if (AttributeValue = NULL_NATURAL) then
                    bytemode := ORACLENULL_NATURAL
                else
                    if (AttributeValue = NULL_EMPTY_STRING) then
                        bytemode := ORACLENULL_EMPTY_STRING
                    else
                        bytemode := ORACLENULL_TO_STRING;
                self.Connection.OracleNulls := bytemode;
            END;
         PREFETCH:
            Begin
                if (AttributeValue = PREFETCH_ON) then
                    mode := QUERY_FREFETCH_ON
                else
                    mode := QUERY_PREFETCH_OFF;
            self.Connection.BufferQuery := mode;
            END;
         COMPRESSION:
            Begin
                if (AttributeValue = COMPRESSION_ON) then
                    mode := OPT_COMPRESS_ON
                else
                    mode := OPT_COMPRESS_OFF;
                self.pre_connect_compression := mode;
            END;
         MULTIQUERY:
            Begin
                if (AttributeValue = MULTIQUERY_ON) then
                    mode := OPT_MULTIQUERY_ON
                else
                    mode := OPT_MULTIQUERY_OFF;
                self.pre_connect_multiquery := mode;
            END;
    end;
end;

function TPDO.assemble_select (Select: PDO_selectRecord): AnsiString;
var
    Dialect: TSQLDialect;
Begin
    Result := '';
    if not self.protocol_defined then exit;

    Dialect := self.protocols[self.protocolIndex].GetSQLDialect;
    if ((Dialect = SQL_MSSQL) or
        (Dialect = SQL_SYBASE) or
        (Dialect = SQL_DB2) or
        (Dialect = SQL_ODBC) or
        (Dialect = SQL_ORACLE)) and
        (Select.sqlOrderBy = '') then
    begin
        self.log_something(lcOther, SNeedOrderBy, 0, SSQLAssemblyError);
        exit;
    end;


    case Dialect of
{$IFDEF ENABLE_MYSQL}
        SQL_MYSQL:      Result := PDOSQL.assemble_select_mysql(Select);
{$ENDIF}
{$IFDEF ENABLE_POSTGRESQL}
        SQL_POSTGRESQL: Result := PDOSQL.assemble_select_pgsql(Select);
{$ENDIF}
{$IFDEF ENABLE_SQLITE}
        SQL_SQLITE:     Result := PDOSQL.assemble_select_sqlite(Select);
{$ENDIF}
{$IFDEF ENABLE_DBLIB}
        SQL_MSSQL,
        SQL_SYBASE:     Result := PDOSQL.assemble_select_mssql (Select);
{$ENDIF}
{$IFDEF ENABLE_DB2}
        SQL_DB2,
        SQL_ODBC:       Result := PDOSQL.assemble_select_db2 (Select);
{$ENDIF}
{$IFDEF ENABLE_ORACLE}
        SQL_ORACLE:     Result := PDOSQL.assemble_select_oracle (Select);
{$ENDIF}
{$IFDEF ENABLE_FIREBIRD}
        SQL_FIREBIRD:   Result := PDOSQL.assemble_select_firebird(Select);
{$ENDIF}
    end;
End;

function TPDO.query_as_is  (sql: AnsiString): IPDOStatement;
Begin
    if self.active then
      begin
        Result := self.Connection.InitializeStatement;
        Result.setSQL(sql, DIRECT_STATEMENT);
        Result.execute;
      end
    else
      begin
        Result := nil;
        self.log_something(lcExecute, sql, 0, SQueryWithOutConnect);
      end;
End;

function TPDO.query_select(sql: PDO_selectRecord): IPDOStatement;
var
    assembled_sql: ansistring;
Begin
    assembled_sql := self.assemble_select(sql);
    Result := self.query_as_is(assembled_sql);
End;

function TPDO.prepare_as_is(sql: AnsiString):IPDOStatement;
Begin
    if self.active then
      begin
        Result := self.Connection.InitializeStatement;
        Result.setSQL(sql, PREPARED_STATEMENT);
      end
    else
      begin
        Result := nil;
        self.log_something(lcPrepStmt, sql, 0, SQueryWithOutConnect);
      end;
End;

function TPDO.prepare_select(sql: PDO_selectRecord):IPDOStatement;
var
    assembled_sql: ansistring;
Begin
    assembled_sql := self.assemble_select(sql);
    Result := self.prepare_as_is(assembled_sql);
End;

procedure TPDO.setMaxBLOBSize(BLOBSize: Int64);
Begin
    self.Connection.MaxBLOBSize := BLOBSize;
End;

procedure TPDO.clear_listener;
begin
    self.Listener := nil;
end;

{
    Let's handle the listener this way:
    In order to capture attempts to query the database after the connection was closed,
    we'll leave the listener connected indefinitely.  If a new connection is attempted with
    a different listener, the pointer will be replaced.  If a connection is attempted with no
    listener, then the current listener will be clear.
}
procedure TPDO.add_listener(ListenerHandle: TZAbstractLoggingListener);
begin
    self.Listener := ListenerHandle;
end;


procedure TPDO.log_something (const Category: TZLoggingCategory; const msg: ansistring;
    const error_code: integer = 0; const error_msg: ansistring = '');
var
    silent_mode: byte;
    Event: TZLoggingEvent;
    protocol_description: ansistring;
begin
    if not assigned(self.Listener) then exit;

    if self.active then
      begin
        silent_mode := self.Connection.SilentMode;
        protocol_description := self.Connection.GetDescription;
      end
    else
      begin
        silent_mode := ERRORLEVEL_WARNING;
        protocol_description := SNoConnection;
      end;
    if silent_mode = ERRORLEVEL_SILENT then exit;


    Event := TZLoggingEvent.Create(Category, protocol_description, msg, error_code, error_msg);
    try
      self.Listener.LogEvent(Event);
    finally
        Event.Destroy;
    end;
end;


procedure TPDO.establish_available_protocols;
var
    index: byte;
    n: byte;
begin

    self.numProtocols := 0;
{$IFDEF ENABLE_MYSQL}
    n := self.numProtocols;
    {$IFDEF MYSQL40DRIVER}
    self.numProtocols := self.numProtocols + 1;
    {$ENDIF}

    {$IFDEF MYSQL41DRIVER}
    self.numProtocols := self.numProtocols + 1;
    {$ENDIF}

    {$IFDEF MYSQL50DRIVER}
    self.numProtocols := self.numProtocols + 1;
    {$ENDIF}
    setlength(self.protoMySQL, self.numProtocols - n);
{$ENDIF}
    SetLength(self.protocols,numProtocols);
    SetLength(self.subProtocols, numProtocols);

    index := 0;
{$IFDEF ENABLE_MYSQL}
    n := 0;
    {$IFDEF MYSQL40DRIVER}
    self.protoMySQL[n]         := TMySQL40Driver.Create;
    self.protocols[index]      := self.protoMySQL[n];
    self.subProtocols[index]   := n;
    index := index + 1;
    n := n + 1;
    {$ENDIF}

    {$IFDEF MYSQL41DRIVER}
    self.protoMySQL[n]         := TMySQL41Driver.Create;
    self.protocols[index]      := self.protoMySQL[n];
    self.subProtocols[index]   := n;
    index := index + 1;
    n := n + 1;
    {$ENDIF}

    {$IFDEF MYSQL50DRIVER}
    self.protoMySQL[n]         := TMySQL50Driver.Create;
    self.protocols[index]      := self.protoMySQL[n];
    self.subProtocols[index]   := n;
//    index := index + 1;
//      n := n + 1;
    {$ENDIF}
{$ENDIF}

   self.protocolIndex := -1;

end;


function TPDO.select_protocol (const dsn: ansistring): boolean;
var
    index: shortInt;
begin
    self.protocolIndex := -1;
    Result := false;
    for index := 0 to self.numProtocols - 1 do
        if PDOSysUtils.StartsWith(dsn, Format('pdo:%s://', [self.protocols[index].GetProtocol])) then
        begin
            self.protocolIndex := index;
            Result := true;
            break;
        end;
end;

function TPDO.initialize_parameters (const dsn, user_id, password: ansistring;
    const options: longword; info: TStringList): boolean;
var
  workstr, ns, proto: AnsiString;
  dx: Word;
  missing_equal: boolean;
begin
    info.Clear;

    info.Add('username=' + user_id);
    info.Add('password=' + Password);
    info.Add('options=' + inttostr(options));

    workstr := copy(dsn,5,length(dsn)-4);
    dx := AnsiPos(':',workstr);
    workstr := copy(workstr, dx + 3, (length(workstr)- dx - 2)) + ';';

    missing_equal := false;
    while (Length(workstr) > 0) do
    begin
        dx := AnsiPos(';',workstr);
        ns := copy (workstr, 0, dx - 1);
        workstr := copy (workstr, dx + 1, MaxInt);
        if (ns <> '') then
        begin
            if (AnsiPos('=', ns) > 0) then
                info.Add(ns)
            else
                missing_equal := true;
        end;
    end;
    if (missing_equal) then
        begin
            Result := false;
            proto  := self.protocols[self.protocolIndex].GetProtocol;
            self.log_something(lcConnect, proto, 0, SDSNMissingEqual);
        end
    else
        Result := true;

end;

function TPDO.establish_connection (info: TStringList): IZConnection;
var
    Dialect: TSQLDialect;
begin
    Dialect := self.protocols[self.protocolIndex].GetSQLDialect;
    Result := nil;
    case Dialect of
    {$IFDEF ENABLE_MYSQL}
        SQL_MYSQL: Result := PDOConnectMySQL.TMySQLConnect.Create(info,  self.protoMySQL[self.subProtocols[self.protocolIndex]]);
    {$ENDIF}
    end;
end;

function TPDO.protocol_defined: boolean;
begin
    if self.protocolIndex < 0 then
        begin
            self.log_something(lcOther, SEstablishDialect, 0, SSQLAssemblyError);
            Result := false;
        end
    else
        Result := true;
end;

function TPDO.query_delete (
    const DeletionMethod: TDeletionMethod;
    const tablename: Ansistring;
    const condition: Ansistring = '';
    const limit: longword = 0;
    const order: Ansistring = ''): int64;
var
    Dialect: TSQLDialect;
    sequel: ansistring;
begin
    Result := 0;
    if not self.protocol_defined then exit;

    Dialect := self.protocols[self.protocolIndex].GetSQLDialect;
    case Dialect of
{$IFDEF ENABLE_MYSQL}
        SQL_MYSQL:      sequel := PDOSQLDelete.assemble_delete_mysql(DeletionMethod, tablename, condition, order, limit);
{$ENDIF}
{$IFDEF ENABLE_POSTGRESQL}
        SQL_POSTGRESQL: sequel := PDOSQLDelete.assemble_delete_pgsql(DeletionMethod, tablename, condition, order, limit);
{$ENDIF}
{$IFDEF ENABLE_SQLITE}
        SQL_SQLITE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_DBLIB}
        SQL_MSSQL,
        SQL_SYBASE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_DB2}
        SQL_DB2,
        SQL_ODBC:       sequel := '';
{$ENDIF}
{$IFDEF ENABLE_ORACLE}
        SQL_ORACLE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_FIREBIRD}
        SQL_FIREBIRD:
            begin
                if self.protocols[self.protocolIndex].GetProtocol = 'firebird-2.0' then
                    sequel := PDOSQLDelete.assemble_delete_firebird_20(DeletionMethod, tablename, condition, order, limit)
                else
                    sequel := PDOSQLDelete.assemble_delete_firebird_15(DeletionMethod, tablename, condition, order, limit);
            end;
{$ENDIF}
    end;

    Result := self.execute_now(sequel);
end;

function TPDO.query_delete_multitable (
    const tables: Ansistring;
    const table_joins: Ansistring;
    const condition: ansistring = ''): int64;
var
    Dialect: TSQLDialect;
    sequel: ansistring;
begin
    Result := 0;
    if not self.protocol_defined then exit;

    Dialect := self.protocols[self.protocolIndex].GetSQLDialect;
    try
        case Dialect of
{$IFDEF ENABLE_MYSQL}
            SQL_MYSQL:
                begin
                    if self.protocols[self.protocolIndex].GetProtocol = 'mysql-4.0' then
                        raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable'])
                    else
                        sequel := PDOSQLDelete.assemble_multidelete_mysql(tables, table_joins, condition);
                end;
{$ENDIF}
{$IFDEF ENABLE_POSTGRESQL}
            SQL_POSTGRESQL: sequel := PDOSQLDelete.assemble_multidelete_pgsql(tables, table_joins, condition);
{$ENDIF}
{$IFDEF ENABLE_SQLITE}
            SQL_SQLITE:     raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable']);
{$ENDIF}
{$IFDEF ENABLE_DBLIB}
            SQL_MSSQL,
            SQL_SYBASE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_DB2}
            SQL_DB2,
            SQL_ODBC:       sequel := '';
{$ENDIF}
{$IFDEF ENABLE_ORACLE}
            SQL_ORACLE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_FIREBIRD}
            SQL_FIREBIRD:   raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable']);
{$ENDIF}
        end;
        Result := self.execute_now(sequel);
    except
    end;
end;

function TPDO.query_update (
    const tablename: Ansistring;
    const set_expressions: Ansistring;
    const condition: Ansistring = '';
    const order: Ansistring = '';
    const limit: longword = 0): int64;
var
    Dialect: TSQLDialect;
    sequel: ansistring;
begin
    Result := 0;
    if not self.protocol_defined then exit;

    Dialect := self.protocols[self.protocolIndex].GetSQLDialect;
    case Dialect of
{$IFDEF ENABLE_MYSQL}
        SQL_MYSQL:      sequel := PDOSQLUpdate.assemble_update_mysql(tablename, set_expressions, condition, order, limit);
{$ENDIF}
{$IFDEF ENABLE_POSTGRESQL}
        SQL_POSTGRESQL: sequel := PDOSQLUpdate.assemble_update_pgsql(tablename, set_expressions, condition, order, limit);
{$ENDIF}
{$IFDEF ENABLE_SQLITE}
        SQL_SQLITE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_DBLIB}
        SQL_MSSQL,
        SQL_SYBASE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_DB2}
        SQL_DB2,
        SQL_ODBC:       sequel := '';
{$ENDIF}
{$IFDEF ENABLE_ORACLE}
        SQL_ORACLE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_FIREBIRD}
        SQL_FIREBIRD:
            begin
                if self.protocols[self.protocolIndex].GetProtocol = 'firebird-2.0' then
                    sequel := PDOSQLUpdate.assemble_update_firebird_20(tablename, set_expressions, condition, order, limit)
                else
                    sequel := PDOSQLUpdate.assemble_update_firebird_15(tablename, set_expressions, condition, order, limit);
            end;
{$ENDIF}
    end;

    Result := self.execute_now(sequel);
end;

function TPDO.query_update_multitable (
    const table_joins: Ansistring;
    const set_expressions: Ansistring;
    const condition: ansistring = ''): int64;
var
    Dialect: TSQLDialect;
    sequel: ansistring;
begin
    Result := 0;
    if not self.protocol_defined then exit;

    Dialect := self.protocols[self.protocolIndex].GetSQLDialect;
    try
        case Dialect of
{$IFDEF ENABLE_MYSQL}
            SQL_MYSQL:
                begin
                    if self.protocols[self.protocolIndex].GetProtocol = 'mysql-4.0' then
                        raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable'])
                    else
                        sequel := PDOSQLUpdate.assemble_multiupdate_mysql(table_joins, set_expressions, condition);
                end;
{$ENDIF}
{$IFDEF ENABLE_POSTGRESQL}
            SQL_POSTGRESQL: raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable']);
{$ENDIF}
{$IFDEF ENABLE_SQLITE}
            SQL_SQLITE:     raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable']);
{$ENDIF}
{$IFDEF ENABLE_DBLIB}
            SQL_MSSQL,
            SQL_SYBASE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_DB2}
            SQL_DB2,
            SQL_ODBC:       sequel := '';
{$ENDIF}
{$IFDEF ENABLE_ORACLE}
            SQL_ORACLE:     sequel := '';
{$ENDIF}
{$IFDEF ENABLE_FIREBIRD}
            SQL_FIREBIRD:   raise Exception.CreateFmt (SUnsupportedByDriver, ['query_delete_multitable']);
{$ENDIF}
        end;
        Result := self.execute_now(sequel);
    except
    end;
end;

end.
