Thread safety

ParseInput

ParseInput overload 1 (all parameters explicit) is fully thread-safe. Each call operates entirely on its own stack and local variables; no shared state is read or written. It can be called concurrently from any number of threads without synchronization.

// Safe to call from multiple threads simultaneously.
R := ParseInput(Input, itDate, '~', [], sopEqual, sqlMySQL);

Overloads 2–5 read the global variable GDefaultDialect at call time. They are safe to use from multiple threads provided SetSQLDialect is never called after multi-threaded processing has begun. The typical safe pattern is to call SetSQLDialect once during application startup — before any secondary threads are created — and then use overloads 2–5 freely from any thread:

// Main thread, application startup.
SetSQLDialect(sqlElevateDB);

// Worker threads: safe, because SetSQLDialect is never called again.
R := ParseInput(Input, itDate);

If the dialect must change at runtime while threads are running, use overload 1 exclusively and pass the dialect explicitly on each call.

TEgStrParser component

Different instances

Concurrent Parse() calls on different TEgStrParser instances are thread-safe. Each instance stores its own FSqlDialect and passes it directly to ParseInput overload 1. No global variable is read or written.

// Safe: Parser1 and Parser2 are distinct instances.
// Thread A:
R := Parser1.Parse(Input1, itDate);
// Thread B (simultaneously):
R := Parser2.Parse(Input2, itInteger);

Same instance

Concurrent calls on the same TEgStrParser instance are not thread-safe. FLastResult and FLastControl are written on every call without synchronization; concurrent writes produce a data race.

If a single component must be shared across threads, protect each call with a lock:

// One lock per shared component instance.
FCritSect.Enter;
try
  R := Parser1.Parse(Input, itDate);
finally
  FCritSect.Leave;
end;

Alternatively, create a separate TEgStrParser instance per thread, or use ParseInput overload 1 directly in worker threads and reserve the component for the main thread.

SetSQLDialect / GetSQLDialect

SetSQLDialect and GetSQLDialect access the global variable GDefaultDialect without synchronization. Do not call either function from a thread while another thread may be calling ParseInput overloads 2–5 or the other dialect accessor.

// Unsafe: dialect may change while worker thread reads it.
// Worker thread:
R := ParseInput(Input, itDate);   // reads GDefaultDialect
// Main thread (simultaneously):
SetSQLDialect(sqlMySQL);          // writes GDefaultDialect

FormatSettings

ParseInput reads FormatSettings (the global Delphi locale settings) to initialize the per-call SqlFmt and LocaleFmt records. In Delphi 11 and later, FormatSettings is a thread-local variable by default when {$DEFINE POSIX} or the RTL is configured for thread safety. On Windows with the default RTL settings, FormatSettings is a global variable; changing it from one thread while another is calling ParseInput is a data race.

In practice, FormatSettings is normally set once at startup and not changed thereafter, which is safe. If per-thread locale overrides are needed, use the TFormatSettings-based overloads of the RTL functions directly rather than modifying the global.

Summary

Scenario Safe?
ParseInput overload 1, multiple threads Yes
ParseInput overloads 2–5, multiple threads, SetSQLDialect called only at startup Yes
ParseInput overloads 2–5, SetSQLDialect called at runtime from another thread No
TEgStrParser.Parse(), different instances, multiple threads Yes
TEgStrParser.Parse(), same instance, multiple threads No
SetSQLDialect / GetSQLDialect concurrent with overloads 2–5 No
(C) 2026 Easygate, Lda