Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions builds/win32/msvc15/isql_static.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ClCompile Include="..\..\..\src\isql\Extender.cpp" />
<ClCompile Include="..\..\..\gen\isql\extract.cpp" />
<ClCompile Include="..\..\..\src\common\fb_exception.cpp" />
<ClCompile Include="..\..\..\src\isql\FrontendLexer.cpp" />
<ClCompile Include="..\..\..\src\isql\InputDevices.cpp" />
<ClCompile Include="..\..\..\gen\isql\isql.cpp" />
<ClCompile Include="..\..\..\src\isql\iutils.cpp" />
Expand All @@ -38,6 +39,7 @@
<ClInclude Include="..\..\..\src\isql\ColList.h" />
<ClInclude Include="..\..\..\src\isql\Extender.h" />
<ClInclude Include="..\..\..\src\isql\extra_proto.h" />
<ClInclude Include="..\..\..\src\isql\FrontendLexer.h" />
<ClInclude Include="..\..\..\src\isql\InputDevices.h" />
<ClInclude Include="..\..\..\src\isql\isql.h" />
<ClInclude Include="..\..\..\src\isql\isql_proto.h" />
Expand Down
6 changes: 6 additions & 0 deletions builds/win32/msvc15/isql_static.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
<ClCompile Include="..\..\..\src\common\fb_exception.cpp">
<Filter>ISQL files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\isql\FrontendLexer.cpp">
<Filter>ISQL files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\isql\InputDevices.cpp">
<Filter>ISQL files</Filter>
</ClCompile>
Expand Down Expand Up @@ -67,6 +70,9 @@
<ClInclude Include="..\..\..\src\isql\extra_proto.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\isql\FrontendLexer.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\isql\InputDevices.h">
<Filter>Header files</Filter>
</ClInclude>
Expand Down
1 change: 1 addition & 0 deletions builds/win32/msvc15/isql_test.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\isql\tests\FrontendLexerTest.cpp" />
<ClCompile Include="..\..\..\src\isql\tests\ISqlTest.cpp" />
</ItemGroup>
<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions builds/win32/msvc15/isql_test.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\isql\tests\FrontendLexerTest.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\isql\tests\ISqlTest.cpp">
<Filter>source</Filter>
</ClCompile>
Expand Down
68 changes: 67 additions & 1 deletion doc/README.isql_enhancements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ SQL> SET PER_TAB OFF;
Isql enhancements in Firebird v6.
---------------------------------

EXPLAIN statement.
12) EXPLAIN statement.

Author: Adriano dos Santos Fernandes

Expand All @@ -353,3 +353,69 @@ CON> select id from employees where id = ? into id;
CON> end!
SQL>
SQL> set term ;!


13) SET AUTOTERM ON/OFF

Author: Adriano dos Santos Fernandes

When set to ON, terminator defined with SET TERM is changed to semicolon and a new logic
for TERM detection is used, where engine helps ISQL to detect valid usage of semicolons
inside statements.

At each semicolon (outside quotes or comments), ISQL prepares the query buffer with
engine using flag IStatement::PREPARE_REQUIRE_SEMICOLON.

If engine prepares the statement correctly, it's run and ISQL is put in new statement
mode.

If engine returns error isc_command_end_err2, then ISQL is put in statement
continuation mode and asks for another line, repeating the process.

If engine returns a different error, the error is shown and ISQL is put in new statement
mode.

Notes:
- This option can also be activated with command line parameter -autot(erm)
- It can only be used with Firebird engine/server v6 or later
- SET TERM command automatically sets AUTOTERM to OFF
- SET AUTOTERM ON command automatically sets TERM to semicolon
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What terminator will be set after SET AUTOTERM OFF ?
Will it remains semicolon or will be returned to the state as before SET AUTOTERM ON ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put in the docs that it is set to semicolon. For me it was clear that it's not reverted when AUTOTERM is turned OFF, but we may improve it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it was about a bit more clear docs.

- While AUTOTERM ON can be used in non-interactive scripts, at each semicolon,
statement may be tried to be compiled using the server/engine.
That may be slow for big scripts with PSQL statements spanning many lines.

Examples:

SQL> SET AUTOTERM ON;

SQL> execute block returns (o1 integer)
CON> as
CON> begin
CON> o1 = 1;
CON> suspend;
CON> end;

O1
============
1

SQL> select 1 from rdb$database;

CONSTANT
============
1

SQL> select 1
CON> from rdb$database;

CONSTANT
============
1

SQL> select 1
CON> from rdb$database
CON> where true;

CONSTANT
============
1
4 changes: 3 additions & 1 deletion src/dsql/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ using namespace Jrd;


Parser::Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch,
USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT charSetId)
USHORT aClientDialect, USHORT aDbDialect, bool aRequireSemicolon,
const TEXT* string, size_t length, SSHORT charSetId)
: PermanentStorage(pool),
statementPool(aStatementPool),
scratch(aScratch),
client_dialect(aClientDialect),
db_dialect(aDbDialect),
requireSemicolon(aRequireSemicolon),
transformedString(pool),
strMarks(pool),
stmt_ambiguous(false)
Expand Down
4 changes: 3 additions & 1 deletion src/dsql/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ class Parser : public Firebird::PermanentStorage

public:
Parser(thread_db* tdbb, MemoryPool& pool, MemoryPool* aStatementPool, DsqlCompilerScratch* aScratch,
USHORT aClientDialect, USHORT aDbDialect, const TEXT* string, size_t length, SSHORT charSetId);
USHORT aClientDialect, USHORT aDbDialect, bool aRequireSemicolon,
const TEXT* string, size_t length, SSHORT charSetId);
~Parser();

public:
Expand Down Expand Up @@ -363,6 +364,7 @@ class Parser : public Firebird::PermanentStorage
DsqlCompilerScratch* scratch;
USHORT client_dialect;
USHORT db_dialect;
const bool requireSemicolon;
USHORT parser_version;
CharSet* charSet;

Expand Down
36 changes: 21 additions & 15 deletions src/dsql/dsql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ using namespace Firebird;

static ULONG get_request_info(thread_db*, DsqlRequest*, ULONG, UCHAR*);
static dsql_dbb* init(Jrd::thread_db*, Jrd::Attachment*);
static DsqlRequest* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool);
static DsqlRequest* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, unsigned, bool);
static RefPtr<DsqlStatement> prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT,
bool, ntrace_result_t* traceResult);
unsigned, bool, ntrace_result_t* traceResult);
static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const);
static void sql_info(thread_db*, DsqlRequest*, ULONG, const UCHAR*, ULONG, UCHAR*);
static UCHAR* var_info(const dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*,
Expand Down Expand Up @@ -261,7 +261,7 @@ DsqlRequest* DSQL_prepare(thread_db* tdbb,
// Allocate a new request block and then prepare the request.

dsqlRequest = prepareRequest(tdbb, database, transaction, length, string, dialect,
isInternalRequest);
prepareFlags, isInternalRequest);

// Can not prepare a CREATE DATABASE/SCHEMA statement

Expand Down Expand Up @@ -336,7 +336,7 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr
try
{
dsqlRequest = prepareRequest(tdbb, database, *tra_handle, length, string, dialect,
isInternalRequest);
0, isInternalRequest);

const auto dsqlStatement = dsqlRequest->getDsqlStatement();

Expand Down Expand Up @@ -443,15 +443,15 @@ static dsql_dbb* init(thread_db* tdbb, Jrd::Attachment* attachment)
// Prepare a request for execution.
// Note: caller is responsible for pool handling.
static DsqlRequest* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest)
ULONG textLength, const TEXT* text, USHORT clientDialect, unsigned prepareFlags, bool isInternalRequest)
{
TraceDSQLPrepare trace(database->dbb_attachment, transaction, textLength, text, isInternalRequest);

ntrace_result_t traceResult = ITracePlugin::RESULT_SUCCESS;
try
{
auto statement = prepareStatement(tdbb, database, transaction, textLength, text,
clientDialect, isInternalRequest, &traceResult);
clientDialect, prepareFlags, isInternalRequest, &traceResult);

auto dsqlRequest = statement->createRequest(tdbb, database);

Expand All @@ -472,7 +472,8 @@ static DsqlRequest* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra*
// Prepare a statement for execution.
// Note: caller is responsible for pool handling.
static RefPtr<DsqlStatement> prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest, ntrace_result_t* traceResult)
ULONG textLength, const TEXT* text, USHORT clientDialect, unsigned prepareFlags, bool isInternalRequest,
ntrace_result_t* traceResult)
{
Database* const dbb = tdbb->getDatabase();

Expand All @@ -493,15 +494,18 @@ static RefPtr<DsqlStatement> prepareStatement(thread_db* tdbb, dsql_dbb* databas
Arg::Gds(isc_command_end_err2) << Arg::Num(1) << Arg::Num(1));
}

// Get rid of the trailing ";" if there is one.

for (const TEXT* p = text + textLength; p-- > text;)
if (!(prepareFlags & IStatement::PREPARE_REQUIRE_SEMICOLON))
{
if (*p != ' ')
// Get rid of the trailing ";" if there is one.

for (const TEXT* p = text + textLength; p-- > text;)
{
if (*p == ';')
textLength = p - text;
break;
if (*p != ' ')
{
if (*p == ';')
textLength = p - text;
break;
}
}
}

Expand Down Expand Up @@ -556,7 +560,9 @@ static RefPtr<DsqlStatement> prepareStatement(thread_db* tdbb, dsql_dbb* databas
scratch->flags |= DsqlCompilerScratch::FLAG_INTERNAL_REQUEST;

Parser parser(tdbb, *scratchPool, statementPool, scratch, clientDialect,
dbDialect, text, textLength, charSetId);
dbDialect,
(prepareFlags & IStatement::PREPARE_REQUIRE_SEMICOLON),
text, textLength, charSetId);

// Parse the SQL statement. If it croaks, return
dsqlStatement = parser.parse();
Expand Down
11 changes: 9 additions & 2 deletions src/dsql/parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -868,8 +868,15 @@ using namespace Firebird;
// list of possible statements

top
: statement { parsedStatement = $1; }
| statement ';' { parsedStatement = $1; }
: statement
{
if (requireSemicolon)
yyerrorIncompleteCmd(YYPOSNARG(1));

parsedStatement = $1;
}
| statement ';'
{ parsedStatement = $1; }
;

%type <dsqlStatement> statement
Expand Down
1 change: 1 addition & 0 deletions src/include/firebird/FirebirdInterface.idl
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ interface Statement : ReferenceCounted
const uint PREPARE_PREFETCH_DETAILED_PLAN = 0x10;
const uint PREPARE_PREFETCH_AFFECTED_RECORDS = 0x20; // not used yet
const uint PREPARE_PREFETCH_FLAGS = 0x40;
const uint PREPARE_REQUIRE_SEMICOLON = 0x80;
const uint PREPARE_PREFETCH_METADATA =
PREPARE_PREFETCH_TYPE | PREPARE_PREFETCH_FLAGS |
PREPARE_PREFETCH_INPUT_PARAMETERS | PREPARE_PREFETCH_OUTPUT_PARAMETERS;
Expand Down
1 change: 1 addition & 0 deletions src/include/firebird/IdlFbInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -1914,6 +1914,7 @@ namespace Firebird
static CLOOP_CONSTEXPR unsigned PREPARE_PREFETCH_DETAILED_PLAN = 0x10;
static CLOOP_CONSTEXPR unsigned PREPARE_PREFETCH_AFFECTED_RECORDS = 0x20;
static CLOOP_CONSTEXPR unsigned PREPARE_PREFETCH_FLAGS = 0x40;
static CLOOP_CONSTEXPR unsigned PREPARE_REQUIRE_SEMICOLON = 0x80;
static CLOOP_CONSTEXPR unsigned PREPARE_PREFETCH_METADATA = IStatement::PREPARE_PREFETCH_TYPE | IStatement::PREPARE_PREFETCH_FLAGS | IStatement::PREPARE_PREFETCH_INPUT_PARAMETERS | IStatement::PREPARE_PREFETCH_OUTPUT_PARAMETERS;
static CLOOP_CONSTEXPR unsigned PREPARE_PREFETCH_ALL = IStatement::PREPARE_PREFETCH_METADATA | IStatement::PREPARE_PREFETCH_LEGACY_PLAN | IStatement::PREPARE_PREFETCH_DETAILED_PLAN | IStatement::PREPARE_PREFETCH_AFFECTED_RECORDS;
static CLOOP_CONSTEXPR unsigned FLAG_HAS_CURSOR = 0x1;
Expand Down
2 changes: 2 additions & 0 deletions src/include/firebird/impl/msg/isql.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,5 @@ FB_IMPL_MSG_SYMBOL(ISQL, 202, NO_PUBLICATIONS, "There is no publications in this
FB_IMPL_MSG_SYMBOL(ISQL, 203, MSG_PUBLICATIONS, "Publications:")
FB_IMPL_MSG_SYMBOL(ISQL, 204, MSG_PROCEDURES, "Procedures:")
FB_IMPL_MSG_SYMBOL(ISQL, 205, HLP_EXPLAIN, "EXPLAIN -- explain a query access plan")
FB_IMPL_MSG_SYMBOL(ISQL, 206, USAGE_AUTOTERM, " -autot(erm) use auto statement terminator (set autoterm on)")
FB_IMPL_MSG_SYMBOL(ISQL, 207, AUTOTERM_NOT_SUPPORTED, "SET AUTOTERM ON is not supported in engine/server and has been disabled")
1 change: 1 addition & 0 deletions src/include/gen/Firebird.pas
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ IStatement = class(IReferenceCounted)
const PREPARE_PREFETCH_DETAILED_PLAN = Cardinal($10);
const PREPARE_PREFETCH_AFFECTED_RECORDS = Cardinal($20);
const PREPARE_PREFETCH_FLAGS = Cardinal($40);
const PREPARE_REQUIRE_SEMICOLON = Cardinal($80);
const PREPARE_PREFETCH_METADATA = Cardinal(IStatement.PREPARE_PREFETCH_TYPE or IStatement.PREPARE_PREFETCH_FLAGS or IStatement.PREPARE_PREFETCH_INPUT_PARAMETERS or IStatement.PREPARE_PREFETCH_OUTPUT_PARAMETERS);
const PREPARE_PREFETCH_ALL = Cardinal(IStatement.PREPARE_PREFETCH_METADATA or IStatement.PREPARE_PREFETCH_LEGACY_PLAN or IStatement.PREPARE_PREFETCH_DETAILED_PLAN or IStatement.PREPARE_PREFETCH_AFFECTED_RECORDS);
const FLAG_HAS_CURSOR = Cardinal($1);
Expand Down
Loading