diff --git a/packages/fcl-db/src/base/dsparams.inc b/packages/fcl-db/src/base/dsparams.inc index 3fcc192f4c..7a3f6701a2 100644 --- a/packages/fcl-db/src/base/dsparams.inc +++ b/packages/fcl-db/src/base/dsparams.inc @@ -1,4 +1,27 @@ +procedure SkipQuotesString(var p : pchar; QuoteChar : char; EscapeSlash, EscapeRepeat : Boolean); +var notRepeatEscaped : boolean; +begin + Inc(p); + repeat + notRepeatEscaped := True; + while not (p^ in [#0, QuoteChar]) do + begin + if EscapeSlash and (p^='\') and (p[1] <> #0) then Inc(p,2) // make sure we handle \' and \\ correct + else Inc(p); + end; + if p^=QuoteChar then + begin + Inc(p); // skip final ' + if (p^=QuoteChar) and EscapeRepeat then // Handle escaping by '' + begin + notRepeatEscaped := False; + inc(p); + end + end; + until notRepeatEscaped; +end; + { TParams } Function TParams.GetItem(Index: Integer): TParam; @@ -177,36 +200,20 @@ begin end; function SkipComments(var p: PChar; EscapeSlash, EscapeRepeat : Boolean) : Boolean; -var notRepeatEscaped : boolean; - - procedure SkipQuotesString(QuoteChar : char); - begin - Inc(p); - Result := True; - repeat - notRepeatEscaped := True; - while not (p^ in [#0, QuoteChar]) do - begin - if EscapeSlash and (p^='\') and (p[1] <> #0) then Inc(p,2) // make sure we handle \' and \\ correct - else Inc(p); - end; - if p^=QuoteChar then - begin - Inc(p); // skip final ' - if (p^=QuoteChar) and EscapeRepeat then // Handle escaping by '' - begin - notRepeatEscaped := False; - inc(p); - end - end; - until notRepeatEscaped; - end; begin result := false; case p^ of - '''': SkipQuotesString(''''); // single quote delimited string - '"': SkipQuotesString('"'); // double quote delimited string + '''': + begin + SkipQuotesString(p,'''',EscapeSlash,EscapeRepeat); // single quote delimited string + Result := True; + end; + '"': + begin + SkipQuotesString(p,'"',EscapeSlash,EscapeRepeat); // double quote delimited string + Result := True; + end; '-': // possible start of -- comment begin Inc(p); @@ -295,10 +302,21 @@ begin end else begin - ParamNameStart:=p; - while not (p^ in (SQLDelimiterCharacters+[#0,'=','+','-','*','\','/','[',']','|'])) do - Inc(p); - ParamName:=Copy(ParamNameStart,1,p-ParamNameStart); + if p^='"' then // Check if the parameter-name is between quotes + begin + ParamNameStart:=p; + SkipQuotesString(p,'"',EscapeSlash,EscapeRepeat); + // Do not include the quotes in ParamName, but they must be included + // when the parameter is replaced by some place-holder. + ParamName:=Copy(ParamNameStart+1,1,p-ParamNameStart-2); + end + else + begin + ParamNameStart:=p; + while not (p^ in (SQLDelimiterCharacters+[#0,'=','+','-','*','\','/','[',']','|'])) do + Inc(p); + ParamName:=Copy(ParamNameStart,1,p-ParamNameStart); + end; end; end else diff --git a/packages/fcl-db/src/sqldb/sqldb.pp b/packages/fcl-db/src/sqldb/sqldb.pp index 18eac61b51..57c4a06e8c 100644 --- a/packages/fcl-db/src/sqldb/sqldb.pp +++ b/packages/fcl-db/src/sqldb/sqldb.pp @@ -1366,7 +1366,7 @@ var FieldNamesQuoteChar : char; if (pfInKey in Fields[x].ProviderFlags) or ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then - sql_where := sql_where + '(' + FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar + '= :OLD_' + fields[x].FieldName + ') and '; + sql_where := sql_where + '(' + FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar + '= :' + FieldNamesQuoteChar + 'OLD_' + fields[x].FieldName + FieldNamesQuoteChar +') and '; end; function ModifyRecQuery : string; @@ -1383,7 +1383,7 @@ var FieldNamesQuoteChar : char; UpdateWherePart(sql_where,x); if (pfInUpdate in Fields[x].ProviderFlags) then - sql_set := sql_set +FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar +'=:' + fields[x].FieldName + ','; + sql_set := sql_set +FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar +'=:' + FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar + ','; end; if length(sql_set) = 0 then DatabaseErrorFmt(sNoUpdateFields,['update'],self); @@ -1408,7 +1408,7 @@ var FieldNamesQuoteChar : char; if (not fields[x].IsNull) and (pfInUpdate in Fields[x].ProviderFlags) then begin sql_fields := sql_fields + FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar + ','; - sql_values := sql_values + ':' + fields[x].FieldName + ','; + sql_values := sql_values + ':' + FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar +','; end; end; if length(sql_fields) = 0 then DatabaseErrorFmt(sNoUpdateFields,['insert'],self); diff --git a/packages/fcl-db/tests/testbasics.pas b/packages/fcl-db/tests/testbasics.pas index 1ea05f60f2..5501054caa 100644 --- a/packages/fcl-db/tests/testbasics.pas +++ b/packages/fcl-db/tests/testbasics.pas @@ -37,6 +37,7 @@ var Params : TParams; pb : TParamBinding; begin Params := TParams.Create; + AssertEquals( 'select * from table where id = $1', params.ParseSQL('select * from table where id = :id',true,True,True,psPostgreSQL)); @@ -95,6 +96,14 @@ begin AssertEquals( 'select * from table where "id = :id\', params.ParseSQL('select * from table where "id = :id\',true,True,True,psInterbase)); +// Test strange-field names + AssertEquals( 'select * from table where "field-name" = ?', + params.ParseSQL('select * from table where "field-name" = :"field-name"',true,True,True,psInterbase)); + AssertEquals('field-name',Params.Items[0].Name); + + AssertEquals( 'select * from table where "field-name" = ?', + params.ParseSQL('select * from table where "field-name" = :"field-name',true,True,True,psInterbase)); + Params.Free; end; diff --git a/packages/fcl-db/tests/testfieldtypes.pas b/packages/fcl-db/tests/testfieldtypes.pas index 1917850c1b..142e14c0e6 100644 --- a/packages/fcl-db/tests/testfieldtypes.pas +++ b/packages/fcl-db/tests/testfieldtypes.pas @@ -34,6 +34,7 @@ type procedure TestParseUnion; // bug 8442 procedure TestInsertLargeStrFields; // bug 9600 procedure TestNumericNames; // Bug9661 + procedure TestApplyUpdFieldnames; // Bug 12275; procedure Test11Params; procedure TestRowsAffected; // bug 9758 procedure TestStringsReplace; @@ -1132,6 +1133,35 @@ begin end; end; +procedure TTestFieldTypes.TestApplyUpdFieldnames; +begin + with TSQLDBConnector(DBConnector) do + begin + AssertEquals(-1,query.RowsAffected); + Connection.ExecuteDirect('create table FPDEV2 ( ' + + ' ID INT NOT NULL , ' + + ' "NAME-TEST" VARCHAR(250), ' + + ' PRIMARY KEY (ID) ' + + ') '); +// Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections + TSQLDBConnector(DBConnector).Transaction.CommitRetaining; + Connection.ExecuteDirect('insert into FPDEV2(ID,"NAME-TEST") values (1,''test1'')'); + Query.SQL.Text := 'select * from fpdev2'; + Query.Open; + AssertEquals(1,Query.FieldByName('ID').AsInteger); + AssertEquals('test1',Query.FieldByName('NAME-TEST').AsString); + Query.Edit; + Query.FieldByName('NAME-TEST').AsString:='Edited'; + Query.Post; + Query.ApplyUpdates; + Query.Close; + Query.Open; + AssertEquals(1,Query.FieldByName('ID').AsInteger); + AssertEquals('Edited',Query.FieldByName('NAME-TEST').AsString); + Query.Close; + end; +end; + procedure TTestFieldTypes.TestRowsAffected; begin with TSQLDBConnector(DBConnector) do