PrG_query_libraries.xml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE sect1 PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
  3. "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
  4. <sect1 id="Query_Libraries">
  5. <title><emphasis role="bold">Query Libraries</emphasis></title>
  6. <para>A Query Library is a set of attributes, packaged together in a self
  7. contained unit, which allows the code to be shared between different
  8. workunits. This reduces the time required to deploy a set of attributes, and
  9. can reduce the memory footprint for the set of queries within Roxie that use
  10. the library. It is also possible to update a query library without having to
  11. re-deploy all the queries that use it.</para>
  12. <para>Query libraries are not supported in Thor, but may be in the
  13. future.</para>
  14. <para>A Query Library is defined by two structures—an INTERFACE to define
  15. the parameters to pass, and a MODULE that implements the INTERFACE.</para>
  16. <sect2 id="Library_INTERFACE_Definition">
  17. <title>Library INTERFACE Definition</title>
  18. <para>To create a Query Library, the first requirement is a definition of
  19. its input parameters with an INTERFACE structure that receives the
  20. parameters:</para>
  21. <programlisting>NamesRec := RECORD
  22. INTEGER1 NameID;
  23. STRING20 FName;
  24. STRING20 LName;
  25. END;
  26. FilterLibIface1(DATASET(namesRec) ds, STRING search) := INTERFACE
  27. EXPORT DATASET(namesRec) matches;
  28. EXPORT DATASET(namesRec) others;
  29. END;</programlisting>
  30. <para>This example defines the INTERFACE for a library that takes two
  31. inputs—a DATASET (with the specified layout format) and a STRING—and which
  32. gives you the ability to output two DATASET results.</para>
  33. <para>For most library queries it may be preferable to also use a separate
  34. INTERFACE to define the input parameters. Using an INTERFACE makes the
  35. passed parameters clearer and makes it easier to keep the interface and
  36. implementation in sync. This example defines the same library interface as
  37. above:</para>
  38. <programlisting>NamesRec := RECORD
  39. INTEGER1 NameID;
  40. STRING20 FName;
  41. STRING20 LName;
  42. END;
  43. IFilterArgs := INTERFACE //defines passed parameters
  44. EXPORT DATASET(namesRec) ds;
  45. EXPORT STRING search;
  46. END;
  47. FilterLibIface2(IFilterArgs args) := INTERFACE
  48. EXPORT DATASET(namesRec) matches;
  49. EXPORT DATASET(namesRec) others;
  50. END;</programlisting>
  51. </sect2>
  52. <sect2 id="Library_MODULE_Definitions">
  53. <title>Library MODULE Definitions</title>
  54. <para>A query library is a MODULE structure definition that implements a
  55. particular library INTERFACE definition. The parameters passed to the
  56. MODULE must exactly match the parameters for the library INTERFACE
  57. definition, and the MODULE must contain compatible EXPORT attribute
  58. definitions for each of the results specified in the library INTERFACE.
  59. The LIBRARY option on the MODULE definition specifies the library
  60. INTERFACE being implemented. This example defines an implementation for
  61. the INTERFACEs above:</para>
  62. <programlisting>FilterDsLib1(DATASET(namesRec) ds,
  63. STRING search) := MODULE,LIBRARY(FilterLibIface1)
  64. EXPORT matches := ds(Lname = search);
  65. EXPORT others := ds(Lname != search);
  66. END;</programlisting>
  67. <para>and for the variety that takes an INTERFACE as its single
  68. parameter:</para>
  69. <programlisting>FilterDsLib2(IFilterArgs args) := MODULE,LIBRARY(FilterLibIface2)
  70. EXPORT matches := args.ds(Lname = args.search);
  71. EXPORT others := args.ds(Lname != args.search);
  72. END;</programlisting>
  73. </sect2>
  74. <sect2 id="Building_an_External_library">
  75. <title>Building an External library</title>
  76. <para>A query library may be either internal or external. An internal
  77. library would be primarily used in hthor queries for testing and debugging
  78. before deploying to Roxie. Although you can use internal query libraries
  79. in Roxie queries, the advantages come from using the external
  80. version.</para>
  81. <para>An external query library is created by the BUILD action, which
  82. compiles the query library into its own workunit. The name of the library
  83. is the job name associated with the workunit. Therefore, the #WORKUNIT
  84. would normally be used to give the workunit a meaningful job name, as in
  85. this example:</para>
  86. <programlisting>#WORKUNIT('name','Ppass.FilterDsLib');
  87. BUILD(FilterDsLib1);</programlisting>
  88. <para>This code builds the library for the INTERFACE parameter version of
  89. the code above:</para>
  90. <programlisting>#WORKUNIT('name','Ipass.FilterDsLib');
  91. BUILD(FilterDsLib2);</programlisting>
  92. <para>The system maintains a catalog of the latest versions of each query
  93. library that is updated whenever a library is built. Hthor uses this to
  94. resolve query libraries when running a query (as will Thor, when it
  95. eventually supports query libraries). Roxie uses the query aliasing
  96. mechanism in the same way.</para>
  97. </sect2>
  98. <sect2 id="Using_a_Query_Library">
  99. <title>Using a Query Library</title>
  100. <para>The syntax for using a query library is slightly different depending
  101. on whether the library is internal or external. However, both methods use
  102. the LIBRARY function.</para>
  103. <para>The LIBRARY function returns a MODULE implementation with the proper
  104. parameters passed for the instance in which you want to use it, which may
  105. be used to access the EXPORT attributes from the library.</para>
  106. <sect3 id="RoxieQuery_InternalLibraries">
  107. <title id="RoxieQueryLibrary_InternalLibraries">Internal
  108. Libraries</title>
  109. <para>An internal library generates the library code as a separate unit,
  110. but then includes that unit within the query workunit. It doesn't have
  111. the advantage of reducing compile time or memory usage in Roxie, but it
  112. does retain the library structure, which means that changes to the code
  113. cannot affect anyone else using the system. That makes internal
  114. libraries a perfect testing method.</para>
  115. <para>The syntax for using an internal query library simply passes the
  116. library MODULE attribute's name inside an INTERNAL function call in the
  117. first parameter to the LIBRARY function, as in this example (for the
  118. version that does not take an INTERFACE as its parameter):</para>
  119. <programlisting>NamesTable := DATASET([ {1,'Doc','Holliday'},
  120. {2,'Liz','Taylor'},
  121. {3,'Mr','Nobody'},
  122. {4,'Anywhere','but here'}],
  123. NamesRec);
  124. lib1 := LIBRARY(INTERNAL(FilterDsLib1),FilterLibIface1(NamesTable, 'Holliday'));
  125. </programlisting>
  126. <para>In this case, result is a MODULE with two EXPORTed
  127. attributes—matches and others—that can be used just like any other
  128. MODULE, as in this example:</para>
  129. <programlisting>OUTPUT(lib1.matches);
  130. OUTPUT(lib1.others);</programlisting>
  131. <para>and the code changes to this for the variety that takes an
  132. INTERFACE:</para>
  133. <programlisting>NamesTable := DATASET([ {1,'Doc','Holliday'},
  134. {2,'Liz','Taylor'},
  135. {3,'Mr','Nobody'},
  136. {4,'Anywhere','but here'}],
  137. NamesRec);
  138. SearchArgs := MODULE(IFilterArgs)
  139. EXPORT DATASET(namesRec) ds := NamesTable;
  140. EXPORT STRING search := 'Holliday';
  141. END;
  142. lib3 := LIBRARY(INTERNAL(FilterDsLib2),FilterLibIface2(SearchArgs));
  143. OUTPUT(lib3.matches);
  144. OUTPUT(lib3.others);</programlisting>
  145. </sect3>
  146. <sect3 id="RoxieQuery_ExternalLibraries">
  147. <title><emphasis role="bold">External Libraries</emphasis></title>
  148. <para>Once the library is implemented as an external library (using the
  149. BUILD action to create the library is done in a separate workunit) the
  150. LIBRARY function no longer requires the use of the INTERNAL function to
  151. specify the library. Instead, it takes a string constant containing the
  152. name of the workunit created by BUILD as its first parameter, like
  153. this:</para>
  154. <programlisting>NamesTable := DATASET([ {1,'Doc','Holliday'},
  155. {2,'Liz','Taylor'},
  156. {3,'Mr','Nobody'},
  157. {4,'Anywhere','but here'}],
  158. NamesRec);
  159. lib2 := LIBRARY('Ppass.FilterDsLib',FilterLibIface1(NamesTable, 'Holliday'));
  160. OUTPUT(lib2.matches);
  161. OUTPUT(lib2.others);</programlisting>
  162. <para>Or, for the INTERFACE version:</para>
  163. <programlisting>NamesTable := DATASET([ {1,'Doc','Holliday'},
  164. {2,'Liz','Taylor'},
  165. {3,'Mr','Nobody'},
  166. {4,'Anywhere','but here'}],
  167. NamesRec);
  168. SearchArgs := MODULE(IFilterArgs)
  169. EXPORT DATASET(namesRec) ds := NamesTable;
  170. EXPORT STRING search := 'Holliday';
  171. END;
  172. lib4 := LIBRARY('Ipass.FilterDsLib',FilterLibIface2(SearchArgs));
  173. OUTPUT(lib4.matches);
  174. OUTPUT(lib4.others);
  175. </programlisting>
  176. <para>A couple of words of warning about using external libraries: If
  177. you are developing an attribute inside a library that is shared by other
  178. people, then you need to make sure that your development changes don't
  179. invalidate other queries. This means you need to use a different library
  180. name while developing. The simplest method is probably to use a
  181. different attribute for the library implementation while you are
  182. developing. Another way to avoid this is to develop/test with internal
  183. libraries and only build and implement the external library when you are
  184. ready to put the query into production.</para>
  185. <para>If libraries are nested then it gets more complicated. If you are
  186. working on a libraryC, which is called from a libraryA, which is then
  187. called from a query, then you will need to use different library names
  188. for libraryC and libraryA. Otherwise you will either not call your
  189. modified code in libraryC, or everyone using libraryA will call your
  190. modified code. You may prefer to make libraryA and libraryC internal
  191. instead, but you won't gain from the reduced compile time associated
  192. with external libraries.</para>
  193. <para>Also remember your changes are occurring in the library, not in
  194. the query. It's not uncommon to wonder why changes to the ECL aren't
  195. having any effect, and then realize that you've been
  196. rebuilding/deploying the wrong item.</para>
  197. </sect3>
  198. </sect2>
  199. <sect2 id="Query_Library_Tips">
  200. <title>Query Library Tips</title>
  201. <para>You can make your code cleaner by making the LIBRARY call a function
  202. attribute, like this:</para>
  203. <programlisting>FilterDataset(DATASET(namesRecord) ds,
  204. STRING search) := LIBRARY('Ppass.FilterDsLib',FilterLibIface1(ds, search));
  205. </programlisting>
  206. <para>The use of the library then becomes:</para>
  207. <programlisting>FilterDataset(myNames, 'Holliday');</programlisting>
  208. <para>The library name (specified as the first parameter to the LIBRARY
  209. function) does not have to be a constant value, but it must not change
  210. while the query is running. This means you can conditionally select
  211. between different versions of a library.</para>
  212. <para>For example, it is likely that you will want separate libraries for
  213. handling FCRA and non-FCRA data, since combining the two could generate
  214. inefficient or un-processable code. The code for selecting between the two
  215. implementations would look like this:</para>
  216. <programlisting>LibToUse := IF(isFCRA,'special.lookupFRCA','special.lookupNoFCRA);
  217. MyResults := LIBRARY(LibToUse, InterfaceCommonToBoth(args));
  218. </programlisting>
  219. </sect2>
  220. <sect2 id="Query_Library_Restrictions">
  221. <title>Restrictions</title>
  222. <para>The system will report an error if you attempt to use an
  223. implementation of a query library that has a different INTERFACE from the
  224. one specified in the LIBRARY function.</para>
  225. <para>There is one particularly notable restriction on the ECL that can be
  226. included within a library: they cannot include workflow services such as
  227. INDEPENDENT, PERSIST, SUCCESS, and especially, STORED. STORED attributes
  228. don't make sense inside a query library because a query library should be
  229. independent of both the queries that use it, and other query libraries.
  230. Instead of using STORED attributes (like SOAP-enabled Roxie queries use)
  231. to pass parameters to the library queries, the parameters must be
  232. explicitly passed into the query library—either as an individual
  233. parameter, or as part of an INTERFACE definition that provides the
  234. arguments. The query that uses the query library can use stored variables,
  235. and then map those stored variables to the parameters expected by the
  236. query libraries.</para>
  237. <para>Query libraries can currently only EXPORT datasets, datarows, and
  238. single-valued expressions. In particular they cannot EXPORT actions (like
  239. OUTPUT), TRANSFORM structures, or other MODULE structures.</para>
  240. </sect2>
  241. <sect2 id="Notes_on_the_implementation">
  242. <title>Notes on the implementation</title>
  243. <para>There are a couple of items that may be worth noting about the
  244. implementation. In Roxie, before executing the query, all library graphs
  245. are expanded into the query graph. Any datasets that are supplied as
  246. parameters to the library (or a dataset inside an interface parameter) are
  247. directly connected to the place they are used in the query library, and
  248. only results that are used are evaluated. This means that using a query
  249. library should have very little overhead compared with including the ECL
  250. code directly in the query. NOTE: Datasets inside row parameters aren't
  251. streamed, so passing a ROW containing a dataset as a parameter to the
  252. library is not as efficient as using an INTERFACE.</para>
  253. <para>The implementation in hthor is not as efficient. Dataset parameters
  254. are fully evaluated, and passed to the library as a complete unit block
  255. and all results are evaluated. Thor does not yet support query
  256. libraries.</para>
  257. <para>The other item of note is that if libraryA uses libraryC, and
  258. libraryB also uses libraryC with the same parameters, the calls from
  259. different libraries will not be commoned up. However if an attribute
  260. exported from an instance of libraryC is passed to libraryA and libraryB,
  261. then the calls to libraryC will be commoned up. The way attributes
  262. currently tend to be structured in the repository, e.g., calculating
  263. get_Dids() and passing that into other attributes means this is unlikely
  264. to cause any issues in practice.</para>
  265. </sect2>
  266. <sect2 id="Suggested_Structure">
  267. <title>Suggested Structure</title>
  268. <para>Before writing a lot of libraries, it is worth spending some time
  269. working out how the attributes for a library are structured, so all the
  270. libraries in the system are consistent. Here are some guidelines to use
  271. during your query library design phase:</para>
  272. <sect3 id="RoxieQuery_SuggStructure_NamingConventions">
  273. <title>Naming Conventions</title>
  274. <para>I would also suggest coming up with a consistent naming convention
  275. before developing lots of libraries. In particular, you need a
  276. convention for the names of the library arguments, library definition,
  277. implementing module, library implementation and the attribute that wraps
  278. the use of the library. (E.g., something like IXArgs, Xinterface, DoX,
  279. Xlibrary, and X()).</para>
  280. </sect3>
  281. <sect3 id="RoxieQuery_SuggStructure_InterfaceToDefineParameters">
  282. <title>Use an INTERFACE to define parameters</title>
  283. <para>This mechanism (example shown below) provides documentation for
  284. the parameters required by a service. It means the code inside the
  285. implementation will access them as args.xxx or options.xxx, so it will
  286. be clear when parameters are being accessed. It also makes some of the
  287. following suggestions simpler.</para>
  288. </sect3>
  289. <sect3 id="RoxieQuery_SuggStructure_HideTheLibrary">
  290. <title>Hide the LIBRARY</title>
  291. <para>Making the LIBRARY function call a functional attribute (example
  292. also shown below) means you can easily modify all uses of a library if
  293. you are developing a new version. Similarly you can easily switch to use
  294. an internal library instead by changing just the one line of
  295. code.</para>
  296. </sect3>
  297. <sect3 id="RoxieQuery_SuggStructure_UseModuleInheritance">
  298. <title>Use MODULE Inheritance</title>
  299. <para>Use a MODULE structure (without the LIBRARY option) that
  300. implements the library's INTERFACE, and a separate MODULE derived from
  301. the first to implement the LIBRARY using that service module. By hiding
  302. the LIBRARY and using a separate MODULE implementation you can easily
  303. remove the library all together. Also, using a separate implementation
  304. from the library definitions means you can easily generate multiple
  305. variants of the same library from the same definition.</para>
  306. <programlisting>NamesRec := RECORD
  307. INTEGER1 NameID;
  308. STRING20 FName;
  309. STRING20 LName;
  310. END;
  311. NamesTable := DATASET([ {1,'Doc','Holliday'},
  312. {2,'Liz','Taylor'},
  313. {3,'Mr','Nobody'},
  314. {4,'Anywhere','but here'}],
  315. NamesRec);
  316. //define an INTERFACE for the passed parameters
  317. IFilterArgs := INTERFACE
  318. EXPORT DATASET(namesRec) ds;
  319. EXPORT STRING search;
  320. END;
  321. //then define an INTERFACE for the query library
  322. FilterLibIface2(IFilterArgs args) := INTERFACE
  323. EXPORT DATASET(namesRec) matches;
  324. EXPORT DATASET(namesRec) others;
  325. END;
  326. //implement the INTERFACE
  327. FilterDsLib(IFilterArgs args) := MODULE
  328. EXPORT matches := args.ds(Lname = args.search);
  329. EXPORT others := args.ds(Lname != args.search);
  330. END;
  331. //then derive that MODULE to implement the LIBRARY
  332. FilterDsLib2(IFilterArgs args) := MODULE(FilterDsLib(args)),LIBRARY(FilterLibIface2)
  333. END;
  334. //make the LIBRARY call a function
  335. FilterDs(IFilterArgs args) := LIBRARY(INTERNAL(FilterDsLib2),FilterLibIface2(args));
  336. //easily modified to eliminate the LIBRARY, if desired
  337. // FilterDs(IFilterArgs args) := FilterDsLib2(args));
  338. //define the parameters to pass as the interface
  339. SearchArgs := MODULE(IFilterArgs)
  340. EXPORT DATASET(namesRec) ds := NamesTable;
  341. EXPORT STRING search := 'Holliday';
  342. END;
  343. //use the LIBRARY, passing the parameters
  344. OUTPUT(FilterDs(SearchArgs).matches);
  345. OUTPUT(FilterDs(SearchArgs).others);</programlisting>
  346. </sect3>
  347. </sect2>
  348. </sect1>