NOTE! QIC will rewrite database objects. It's recommended that you test the features in a non production environment to get used to the concepts.

INSTALL

  • Download the recommended Install.sql from DOWNLOADS.
  • Run the script in SQL Server

UNINSTALL

  • Download the recommended UnInstall.sql from DOWNLOADS.
  • Run the script in SQL Server

GETTING STARTED

By default QIC uses conventions to resolve and compile stored procedures, functions and views. Certain cases are resolved by configuration, but most use cases are resolved by naming conventions. QIC can be extended to change the conventions if suitable.

If you want to dive right, you can try the QUICK START EXAMPLES. I'm developing a complete reference guide to QIC and it will be included as part of the release in further versions. More sample code are included in the release packages from DOWNLOADS.

QUICK START EXAMPLES

SAMPLE - BASIC USE CASE

This shows how to create an interface with delegates and to compile them.
THIS...
SET NOCOUNT ON
GO

IF OBJECT_ID('Test_') IS NOT NULL DROP PROCEDURE Test_
IF OBJECT_ID('Test_ME') IS NOT NULL DROP PROCEDURE Test_ME
IF OBJECT_ID('Test_DEFAULT') IS NOT NULL DROP PROCEDURE Test_DEFAULT
GO

CREATE PROCEDURE Test_ @DelegateName varchar(50)
AS
BEGIN
	RETURN NULL
END
GO

CREATE PROCEDURE Test_ME
AS
BEGIN
	PRINT 'this is interface test_ called by @DelegateName ME '
END
GO

CREATE PROCEDURE Test_DEFAULT
AS
BEGIN
	PRINT 'this is the default delegate for interface test_ '
END
GO

-- Compile
exec qic.Compile

-- Run
exec Test_'ME'
exec Test_'YOU'

PRINT '--'
PRINT 'The generated interface'
PRINT '--'
exec qic.ScriptInterface 'Test_'
PRINT '--'

GO

IF OBJECT_ID('Test_') IS NOT NULL DROP PROCEDURE Test_
IF OBJECT_ID('Test_ME') IS NOT NULL DROP PROCEDURE Test_ME
IF OBJECT_ID('Test_DEFAULT') IS NOT NULL DROP PROCEDURE Test_DEFAULT
GO

...OUTPUTS THIS
this is interface test_ called by @DelegateName ME 
this is the default delegate for interface test_ 
--
The generated interface
--
ALTER PROCEDURE [dbo].[Test_] @DelegateName varchar(50)
AS
BEGIN
	DECLARE @InterfaceReturn int
 
	IF @DelegateName='ME'
	BEGIN
		EXEC @InterfaceReturn = [dbo].[Test_ME] 
		RETURN ISNULL(@InterfaceReturn,0)
	END
	--DEFAULT
	EXEC @InterfaceReturn = [dbo].[Test_DEFAULT] 
	RETURN ISNULL(@InterfaceReturn,0)
 
END
GO
--

SAMPLE - BASIC CONFIG ROUTING

This shows how config routing can be done by convention.
THIS...
SET NOCOUNT ON
GO

IF OBJECT_ID('Test_') IS NOT NULL DROP PROCEDURE Test_
IF OBJECT_ID('Test_ME') IS NOT NULL DROP PROCEDURE Test_ME
IF OBJECT_ID('Test_ME_TEST') IS NOT NULL DROP PROCEDURE Test_ME_TEST
IF OBJECT_ID('Test_ME_PROD') IS NOT NULL DROP PROCEDURE Test_ME_PROD
IF OBJECT_ID('Test_DEFAULT') IS NOT NULL DROP PROCEDURE Test_DEFAULT
IF OBJECT_ID('Test_DEFAULT_TEST') IS NOT NULL DROP PROCEDURE Test_DEFAULT_TEST
GO

CREATE PROCEDURE Test_ @DelegateName varchar(50)
AS
BEGIN
	RETURN NULL
END
GO

CREATE PROCEDURE Test_ME
AS
BEGIN
	PRINT 'this is interface test_ called by @DelegateName ME '
END
GO

CREATE PROCEDURE Test_ME_TEST
AS
BEGIN
	PRINT 'this is interface test_ called by @DelegateName ME when configured for TEST'
END
GO

CREATE PROCEDURE Test_ME_PROD
AS
BEGIN
	PRINT 'this is interface test_ called by @DelegateName ME when configured for PROD'
END
GO


CREATE PROCEDURE Test_DEFAULT
AS
BEGIN
	PRINT 'this is the default delegate for interface test_ '
END
GO

CREATE PROCEDURE Test_DEFAULT_TEST
AS
BEGIN
	PRINT 'this is the default delegate for interface test_ when configured for TEST'
END
GO


PRINT '--'
PRINT 'Without config'
PRINT '--'

exec qic.Compile
exec Test_'ME'
exec Test_'YOU'

PRINT '--'
PRINT 'Config TEST'
PRINT '--'

exec qic.Compile 'TEST'
exec Test_'ME'
exec Test_'YOU'

PRINT '--'
PRINT 'Config PROD'
PRINT '--'

exec qic.Compile 'PROD'
exec Test_'ME'
exec Test_'YOU'

GO

IF OBJECT_ID('Test_') IS NOT NULL DROP PROCEDURE Test_
IF OBJECT_ID('Test_ME') IS NOT NULL DROP PROCEDURE Test_ME
IF OBJECT_ID('Test_ME_TEST') IS NOT NULL DROP PROCEDURE Test_ME_TEST
IF OBJECT_ID('Test_ME_PROD') IS NOT NULL DROP PROCEDURE Test_ME_PROD
IF OBJECT_ID('Test_DEFAULT') IS NOT NULL DROP PROCEDURE Test_DEFAULT
IF OBJECT_ID('Test_DEFAULT_TEST') IS NOT NULL DROP PROCEDURE Test_DEFAULT_TEST
GO

...OUTPUTS THIS
--
Without config
--
this is interface test_ called by @DelegateName ME 
this is the default delegate for interface test_ 
--
Config TEST
--
this is interface test_ called by @DelegateName ME when configured for TEST
this is the default delegate for interface test_ when configured for TEST
--
Config PROD
--
this is interface test_ called by @DelegateName ME when configured for PROD
this is the default delegate for interface test_ 

CORE CONCEPTS

INTERFACES

These are the main thing in QIC. They serve as proxies for SQL statements and they are the objects that will be rewritten by QIC. You declare the interface (parameters, return types and so on) but you do not write any code inside an interface, because QIC will rewrite the implementation and preserve the interface.
Interfaces comes in two flavors: single routed or multi routed. A single routed have only one delegate to handle the call and the multi routed can route calls to different delegates using the parameter @DelegateName.
Two interfaces can not share the same name. If you have two objects in different schemas with the same interface name, you can use configuration to decide which one that should be compiled as the interface.

VIEWS

There are special considerations when using views as interfaces.
  • @DelegateName parameter. Use a column named DelegateName instead.
  • Triggers for INSERT, DELETE and UPDATE will be created. QIC will use conventions to figure out the keys to make UPDATE and DELETE work. Columns named as 'ID' or InterfaceName + 'ID' will be treated as keys. This convention can be changed by overriding KeyResolve_ (see EXTENDING QIC).

CONVENTIONS

  • Object names ending with underscore _ are treated as interfaces.
  • Interfaces containing the parameter @DelegateName (DelegateName if the interface is a view) are multi routed.
  • Interfaces containing aspects or delegates returning (or outputting by output parameters) @DelegateName are multi routed.
  • @DelegateName should be of type varchar(50).
  • Single routed interfaces only injects the DEFAULT delegate.
  • If the InterfaceFactory (see EXTENDING QIC) is not configured, the Interface will be rendered by the default type specific factory.

CONFIGURATION

Interface configuration allows you to:
  • Assign custom factories (see EXTENDING QIC).
  • Filter out which object QIC will compile as interfaces (see COMPILATION & SCRIPTING).
  • Prioritize overrides.
QIC.INTERFACECONFIG
FIELD DESCRIPTION
WhenConfigName Apply if NULL or matching the @ConfigName parameter
WhenSchemaName Apply if NULL or interface is defined in this schema
WhenInterfaceType Apply if NULL or interface is of this database type
WhenInterfaceName Apply if NULL or interface has this name
ThenInterfaceFactory Assign this factory or the default if NULL
ThenPrio Assign this prio

EXAMPLES

This is a single routed interface:
CREATE PROCEDURE Test_
AS
BEGIN
	RETURN NULL
END

This is a multi routed interface:
CREATE PROCEDURE Test_ @DelegateName varchar(50)
AS
BEGIN
	RETURN NULL
END

DELEGATES

Delegates are concrete implementations and they are called by Interfaces. QIC injects calls to the delegates into the interfaces and maps tokens (for example parameters, see TOKENS) between them. Delegates always shares type with interface (a stored procedure interface has stored procedure delegates).

Two delegates can not share the same name within an interface. One of the delegate will override the other. You can use configuration to prioritize which delegate that will be injected into the interface.

CONVENTIONS

  • Object names starting with the name of an interface ending with a something else is a delegate.
  • If there is an additional underscore in the name, the first part will be the delegate name and the second the config name.
  • The default delegate have the delegate name DEFAULT.

CONFIGURATION

Delegate configuration allows you to:
  • Assign custom factories (see EXTENDING QIC).
  • Assign custom tokenmaps (see TOKEN).
  • Filter out which object QIC will inject as delegate (see COMPILATION & SCRIPTING).
  • Prioritize overriding.
QIC.DELEGATECONFIG
FIELD DESCRIPTION
WhenConfigName Apply if NULL or matching the @ConfigName parameter
WhenSchemaName Apply if NULL or delegate is defined in this schema
WhenInterfaceType Apply if NULL or interface is of this database type
WhenInterfaceName Apply if NULL or interface has this name
WhenDelegateName Apply if NULL or delegate has this name
ThenTokenMap Assign this tokenmap
ThenInterfaceFactory Assign this factory or the default if NULL
ThenPrio Assign this prio

EXAMPLES

This is a delegate called by the interface Test_. This delegate will be called when @DelegateName = "ME". This delegate will not be called when the interface is single routed.
CREATE PROCEDURE Test_ME
AS
BEGIN
	PRINT 'this is interface test_ called by @DelegateName ME '
END

This is a delegate called by the interface Test_. This delegate will be called when @DelegateName doesn't match any other delegates.
CREATE PROCEDURE Test_DEFAULT
AS
BEGIN
	PRINT 'this is the default delegate for interface test_ '
END

TOKENS

Parameters, variables and columns are defined as tokens. These are mapped between interfaces and calls to delegates and aspects by name.
You can override the convention by configuring tokenmaps. QIC will then remap tokens to other tokens and even literals. Use the ThenTokenMap-field in the configuration tables to specify a tokenmap for an object.

There are a few tokens with a specific purpose (see SPECIAL TOKENS MATRIX). These will have special meanings and can have you should not use these for other purposes.
SPECIAL TOKENS MATRIX
TOKEN TYPE SELF INJECTED DESCRIPTION
@DelegateName VARCHAR(50) NO Used by multi routed interfaces
@InterfaceName VARCHAR(50) YES{*} Name of the interface
@InterfaceId INT YES{*} Object_Id of the interface
@InterfaceContext XML YES{*} XML containing all the values passed as parameters to the interface

{*} The token will only be injected if an aspect or delegate has declared it as a parameter.

CONVENTIONS

  • Tokens will be mapped by name.

CONFIGURATION

QIC.TOKENMAP
FIELD DESCRIPTION
TokenMap Name of the token map
WhenToken Name of token to be replaced
ThenToken The value used to replace the token

ASPECTS

Aspects are interfaces, but they are injected at different injection points inside the interface. These are called actions and are described by the ASPECT ACTION MATRIX. Some interface types and aspect types are not compatible (see ASPECT ACTION MATRIX).
The Interface itself (not just delegates) can have aspects. This is expressed by configuring an empty string as delegatename. This allows you to inject aspects that modifies the routing.
Views can only have aspects for the interface.
THIS WILL BE INJECTED BEFORE ROUTING
insert into qic.AspectConfig (WhenInterfaceName,WhenDelegateName,WhenActionName, ThenInterfaceName, ThenReturnToken) values ('fn3_','','BEFORE','fn3aspect_','@DelegateName')
THIS WILL NOT BE INJECTED BEFORE ROUTING (ITS INJECTED BEFORE EACH DELEGATE)
insert into qic.AspectConfig (WhenInterfaceName,WhenDelegateName,WhenActionName, ThenInterfaceName, ThenReturnToken) values ('fn3_',NULL,'BEFORE','fn3aspect_','@DelegateName')
ASPECT COMPATABILITY MATRIX
STORED PROCEDURE ASPECT SCALAR ASPECT MULTI-STATEMENT TABLE-VALUE ASPECT INLINE TABLE-VALUE ASPECT VIEW ASPECT
STORED PROCEDURE INTERFACE BEFORE, AFTER BEFORE, AFTER NO NO NO
SCALAR INTERFACE NO BEFORE, AFTER NO NO NO
MULTI-STATEMENT TABLE-VALUE INTERFACE NO BEFORE, AFTER NO NO NO
INLINE TABLE-VALUE INTERFACE NO NO NO NO NO
VIEW INTERFACE BEFORE, BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE, AFTER, AFTER INSERT, AFTER DELETE, AFTER UPDATE BEFORE, BEFORE INSERT, BEFORE DELETE, BEFORE UPDATE, AFTER, AFTER INSERT, AFTER DELETE, AFTER UPDATE NO NO NO

CONFIGURATION

Aspect configuration allows you to:
  • Assign custom factories (see EXTENDING QIC).
  • Assign custom tokenmaps (see TOKEN).
  • Assign custom token for the returnvalue (see TOKEN).
  • Filter out which object QIC will inject as an aspect (see COMPILATION & SCRIPTING).
  • Prioritize (sort) rendering order of aspect calls.
QIC.ASPECTCONFIG
FIELD DESCRIPTION
WhenConfigName Apply if NULL or matching the @ConfigName parameter
WhenSchemaName Apply if NULL or delegate is defined in this schema
WhenInterfaceType Apply if NULL or interface is of this database type
WhenInterfaceName Apply if NULL or interface has this name
WhenDelegateName Apply if NULL or delegate has this name. Empty string applies to the interface
WhenActionName Apply if NULL or action has this name
ThenInterfaceName Assign this interface to the action
ThenReturnToken Assign this token to the return value
ThenTokenMap Assign this tokenmap
ThenCallFactory Assign this factory or the default if NULL
ThenPrio Assign this prio

COMPILATION AND SCRIPTING

To invoke QIC you either compile (alters interfaces) or script (prints alter scripts). Scripting and compiling procedures takes the parameter: @ConfigName. You can omit this parameter if not needed.
The scripting and compiling procedures are divided into three separate use cases:
  • QIC.CompileInternal & QIC.ScriptInternal - Scripts/compiles objects in the QIC schema. Used when extending QIC.
  • QIC.CompileInterface & QIC.ScriptInterface - Scripts/compiles a specific interface.
  • QIC.Compile & QIC.Script - Scripts/compiles a specific interface.

CONFIGURATION

You can use the table QIC.CONFIG to create config names and use MinPrio as a threshold value to include/exclude object specific configuration.
QIC.CONFIG
FIELD DESCRIPTION
ConfigName Name of configuration
MinPrio Sets the threshold for objects configuration. If the objects prio is lower than this value, then it will not be rendered/compiled

EXAMPLE

exec qic.Compile
exec qic.CompileInterface 'Test_'
exec qic.CompileInternal 

exec qic.Script
exec qic.ScriptInterface 'Test_'
exec qic.ScriptInternal

EXTENDING QIC

In order to override or extend QIC's behaviors you use the core concepts, but you create delegates for interfaces inside the QIC schema and the use QIC.CompileInternal or QIC.ScriptInternal to rewrite these interfaces. This topic will be covered in detail in the reference guide in each release, but this feature is missing at the moment.
NOTE Overriding the default behaviors can break QIC, but you should always be able to recover by removing your extension delegates and rerun the install script.

EXTENSION POINTS

  • Interface_ - Resolves interfaces.
  • Aspect_ - Resolves relationships between interfaces and aspects.
  • Delegate_ - Resolves relationships between interfaces and delegates.
  • InterfaceFactory_ - Renders the implementation for an interface. There is one default factory for each supported object type.
  • CallFactory_ - Renders calls to delegates and aspects. There is one default factory for each supported object type.
  • InterfaceNameFactory_ - Resolves the "Interface Name" part of an interface.
  • DelegateNameFactory_ - Resolves the "Delegate Name" part of a delegate.
  • DelegateConfigNameFactory_ - Resolves the "Config Name" part of a delegate.
  • KeyResolve_ - Resolves the keys for view interfaces.

Last edited May 2, 2014 at 3:22 PM by modermodemet, version 21