{"id":667,"date":"2011-01-11T20:20:18","date_gmt":"2011-01-12T01:20:18","guid":{"rendered":"http:\/\/anvil-of-time.com\/wordpress\/?p=667"},"modified":"2021-03-10T19:09:38","modified_gmt":"2021-03-11T00:09:38","slug":"powerbuilder-next-key-service","status":"publish","type":"post","link":"https:\/\/anvil-of-time.com\/powerbuilder\/powerbuilder-next-key-service\/","title":{"rendered":"PowerBuilder &#8211; Next Key Service"},"content":{"rendered":"<p>As an extension of the next key stored procedure article, here is a pretty easy to implement a next surrogate key service to use with the PFC.  If you don&#8217;t use the PFC it is still fairly easy to use with some modification.  You can use this service anytime you want to get a sequential number, such as a document number or a sequence number. These services use a DataStore to call the spcmn_getNextKeyResultSet stored procedure, which returns the next key for the specified table.<\/p>\n<p><strong>Next Key Class (n_cst_nextKey)<\/strong><\/p>\n<p>This is the base class that adds &#8220;Next Key&#8221; functionality to a PFC based PowerBuilder application. This class can be called directly by the developer but is mainly used by the &#8220;Next Key&#8221; Application, DataWindow, and DataStore services.<\/p>\n<p>Example usage<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ reference to the next key object\nn_cst_nextKey lnv_nextKey\n\n\/\/ create the next key object\nlnv_nextKey = create n_cst_nextKey\n\n\/\/ set the next key&#039;s trans object; note: this step is not required as SQLCA is the default\nlnv_nextKey.of_setTransObject (SQLCA)\n\n\/\/ example: getting next key for &quot;Table&quot;\nlong ll_key\nll_key = lnv_nextKey.of_getNextKey (&quot;Table&quot;)\n\n\/\/ example: getting next 10 keys for &quot;Table&quot;\nlong ll_key\nll_key = lnv_nextKey.of_getNextKey (&quot;Table&quot;, 10) \n\n\/\/ destroy the object\ndestroy (lnv_nextKey)<\/pre>\n<p><strong>Next Key Application Service (n_cst_nextKey instance variable added to n_cst_appmanager)<\/strong><\/p>\n<p>This service adds the Next Key class to the PFC application manager class, n_cst_appmanager. When activated, this service adds next key functionality across an entire application. One benefit is that it is instantiated only once and persists the life of the application; therefore, there is very little overhead involved with using this service.<\/p>\n<p>Example usage<\/p>\n<p>In the constructor event of the application manager (typically a descendant of n_cst_appmanager).<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ start the next key service\nof_setNextKey (true)<\/pre>\n<p>After a connection to the database is established, call of_setTransObject( ) to set the trans object. This method need only be called for trans objects other than SQLCA since SQLCA is the default.<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ set the next key&#039;s trans object; note: this step is not required as SQLCA is the default\ngnv_app.inv_nextKey.of_setTransObject (ltr_object)<\/pre>\n<p>From here on out, the next key service can be called from anywhere in the application with a single line.<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ example: getting next key for &quot;Table&quot;\nlong ll_nextKey\nll_nextKey = gnv_app.inv_nextKey.of_getNextKey (&quot;Table&quot;)\n\n\/\/ example: getting next 10 keys for &quot;Table&quot;\nlong ll_nextKey\nll_nextKey = gnv_app.inv_nextKey.of_getNextKey (&quot;Table&quot;, 10)<\/pre>\n<p><strong>Next Key DataWindow Service (n_cst_dwsrv_nextKey added to u_dw)<\/strong><\/p>\n<p>This service adds the Next Key class to the PFC DataWindow control class, u_dw. When activated, this service adds next key functionality for a specific DataWindow control. The main benefit with this service is that key columns in a dataobject can be registered to a table in the constructor event of a DataWindow control, saving the developer from writing the same code for each DataWindow in the application.<\/p>\n<p>There are two ways to register columns with tables: automatic and manual.<\/p>\n<p>Automatic registering allows the writing of generic objects. Registering is done through an assignment in the tag value of a column (e.g., &#8220;nextkey=Table&#8221;). With automatic registering, of_register( ) is called with no arguments.<\/p>\n<p>Manual registering requires the calling of of_register( ) with explicit table names and column names. There is less overhead with this method because the Next Key class does not have to hunt through tag values columns and tables to register.<\/p>\n<p>Example usage<\/p>\n<p>In the constructor event of the DataWindow control.<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ start the next key service\nof_setNextKey(true)\n\n\/\/ set the next key&#039;s trans object; note: this step is not required as SQLCA is the default\ninv_nextKey.of_setTransObject (ltr_object)<\/pre>\n<p>Manual registering<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ manually register columns to tables\ninv_nextKey.of_register (&quot;Table&quot;, &quot;Column&quot;)\ninv_nextKey.of_register (&quot;Table2&quot;, &quot;Column2&quot;)<\/pre>\n<p>Automatic registering<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ automatically register columns to tables based on dataobject tag values\n\/\/ &quot;Column&quot; has tag = &#039;nextkey=Table&#039;\n\/\/ &quot;Column2&quot; has tag = &#039;nextkey=Table2&#039;\ninv_nextKey.of_register( )<\/pre>\n<p>Some advanced examples of getting keys:<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ get next key and apply to all rows in a DataWindow\ndw_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;)\n\n\/\/ get next key and apply to selected rows in a DataWindow based on an expression\ndw_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;, &quot;isNull(Column)&quot;)\n\n\/\/ get next key and apply to all rows in a DataWindow, without remembering the row status\ndw_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;, false)\n\n\/\/ get next key and apply to selected rows in a DataWindow based on an expression, without remembering the row status\ndw_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;, &quot;isNull(Column)&quot;, false)<\/pre>\n<p><strong>Next Key DataStore Service (n_cst_dssrv_nextKey instance variable added to n_ds)<\/strong><\/p>\n<p>This service adds the Next Key class to the PFC DataStore class, n_ds. When activated, this service adds next key functionality for a specific DataStore object. Due to the nature of this object, registering of tables and columns is not available.<\/p>\n<p>Example usage<\/p>\n<pre name=\"code\" class=\"VB\">\/\/ start the next key service\nids_object.of_setNextKey (true)\n\n\/\/ set the next key&#039;s trans object; note: this step is not required as SQLCA is the default\nids_object.inv_nextKey.of_setTransObject (ltr_object)\n\n\/\/ get next key and apply to all rows in a DataStore\nids_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;)\n\n\/\/ get next key and apply to selected rows in a DataStore based on an expression\nids_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;, &quot;isNull(Column)&quot;)\n\n\/\/ get next key and apply to all rows in a DataStore, without remembering the row status\nids_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;, false)\n\n\/\/ get next key and apply to selected rows in a DataStore based on an expression, without remembering the row status\nids_test.inv_nextKey.of_getNextKey(&quot;Table&quot;, &quot;Column&quot;, &quot;isNull(Column)&quot;, false)<\/pre>\n<p><strong>Export of n_cst_dwsrv_nextkey object for Datawindows<\/strong><\/p>\n<pre name=\"code\" class=\"VB\">$PBExportHeader$n_cst_dwsrv_nextkey.sru\n$PBExportComments$Next Key DataWindow Service\nforward\nglobal type n_cst_dwsrv_nextkey from n_cst_dwsrv\nend type\nend forward\n\nglobal type n_cst_dwsrv_nextkey from n_cst_dwsrv\nevent type long ue_insertrow ( long al_row )\nend type\nglobal n_cst_dwsrv_nextkey n_cst_dwsrv_nextkey\n\ntype variables\n\nprotected:\nn_cst_nextKeyAttrib inv_nextKeyTables\nn_cst_nextKey inv_nextKey\nn_tr itr_object\nend variables\n\nforward prototypes\npublic function integer of_settransobject (n_tr atr_object)\npublic function integer of_register (string as_tablename, string as_columnname)\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression)\npublic function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_rememberrowstatus)\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_rememberrowstatus)\npublic function integer of_getnextkey (string as_tablename, string as_columnname)\npublic function integer of_register ()\nend prototypes\n\nevent ue_insertrow;\n\/\/ \ninteger\tli_i\ninteger\tli_numKeys\nlong     ll_nextKey\nstring   ls_tableName, ls_columnName\n\ndwItemStatus le_rowstatus\n\n\/\/ validate the next key application service is in use\nif not isValid(inv_nextKey) then return FAILURE\n\n\/\/ Loop thru all the tables.\nli_numKeys = upperBound (inv_nextKeyTables.is_tableName) \nFor li_i = 1 to li_numKeys\n\n\t\/\/ get table and column names\n\tls_tableName  = inv_nextKeyTables.is_tableName[li_i]\n\tls_columnName = inv_nextKeyTables.is_columnName[li_i]\n\n\t\/\/ get the next key for the table\n\tll_nextKey = inv_nextKey.of_getNextKey(ls_tableName)\n\n\tif ll_nextKey = 0 then return FAILURE\n\n\t\/\/ set the key\n\tif idw_requestor.setItem(al_row, ls_columnName, ll_nextKey) &lt;&gt; 1 then \n\t\treturn FAILURE\n\tend if\n\nnext \n\n\/\/ convert row back from NewModified! to New!.\nle_rowstatus = idw_Requestor.GetItemStatus(al_row, 0, Primary!)\nIf le_rowstatus = NewModified! Then\n\tidw_requestor.setItemStatus(al_row, 0, Primary!, NotModified!)\nend if\n\nreturn al_row\nend event\n\npublic function integer of_settransobject (n_tr atr_object);\n\/\/ \ninteger li_rc\n\n\/\/ validate\nif IsNull (atr_object) or not IsValid (atr_object) then return FAILURE\n\n\/\/ set the trans object\nli_rc = inv_nextKey.of_setTransObject (atr_object)\n\n\/\/ remember the trans object\nif li_rc = 1 then\n\titr_object = atr_object\nend if\n\nreturn li_rc\n\nend function\n\npublic function integer of_register (string as_tablename, string as_columnname);\n\/\/ \nInteger\tli_newUpper\nString\tls_id\n\n\/\/ Verify required reference.\nIf isNull(idw_requestor) Or Not isValid(idw_requestor) Then Return FAILURE\n\n\/\/ Trim the arguments.\nas_tableName = Trim(as_tableName)\nas_columnName = Trim(as_columnName)\n\n\/\/ Verify passed arguments.\nIf isNull(as_tableName) or len(as_tableName) = 0 Then Return FAILURE\nIf isNull(as_columnName) or len(as_columnName) = 0 Then Return FAILURE\n\n\/\/ validate column is in requestor\nls_id = idw_requestor.describe (as_columnName + &quot;.ID&quot;)\nif not isNumber(ls_id) then return FAILURE\n\n\/\/ Establish the boundaries of the array.\nli_newUpper = UpperBound (inv_nextKeyTables.is_tableName) + 1\n\n\/\/ Set the columns.\ninv_nextKeyTables.is_tableName[li_newUpper] = as_tableName\ninv_nextKeyTables.is_columnName[li_newUpper] = as_columnName\n\nReturn SUCCESS\n\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression);\n\/\/ \n\/\/ pass on event with defaults\nreturn this.of_getNextKey(as_tableName, as_columnName, as_expression, true)\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_rememberrowstatus);\n\/\/ \n\/\/ apply keys to a column for all rows in this datawindow\nlong ll_row, ll_rowCount, ll_numKeys, ll_nextKey\ndwItemStatus le_rowStatus\n\n\/\/ get total number of rows\nll_rowCount = idw_requestor.rowCount()\nif ll_rowCount &lt; 1 then return ll_rowCount\n\n\/\/ number of keys = all rows\t\nll_numKeys = ll_rowCount\n\t\n\/\/ get block of keys\nll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)\n\n\/\/ apply keys to all rows\nll_row = 1\ndo while ll_row &gt; 0 and ll_row &lt;= ll_rowCount\n\n\t\/\/ remember row status\n\tif ab_rememberRowStatus then\n\t\tle_rowStatus = idw_Requestor.getItemStatus(ll_row, 0, primary!)\n\tend if\n\n\t\/\/ set the column with the next key\n\tidw_requestor.setItem(ll_row, as_columnName, ll_nextKey)\n\t\n\t\/\/ reset row status to the remembered row status\n\tif ab_rememberRowStatus then\n\t\t\n\t\t\/\/ if the row status was originally new! or notModified!, then changing\n\t\t\/\/ it to notModified! will reset the row status. if the row status\n\t\t\/\/ was either newModified! or dataModified!, then the row should\n\t\t\/\/ already have its original row status.\n\t\t\n\t\tif le_rowStatus = new! or le_rowStatus = notModified! then\n\t\t\tidw_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)\n\t\tend if\n\t\t\n\tend if\n\n\tll_nextKey++\n\tll_row++\n\t\nloop\n\nreturn 1\n\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_rememberrowstatus);\n\/\/ \n\/\/ apply keys to a column for selected rows in a datawindow based\n\/\/ based on an expression\nlong ll_row, ll_rowCount, ll_numKeys, ll_nextKey\ndwItemStatus le_rowStatus\n\n\/\/ get total number of rows\nll_rowCount = idw_requestor.rowCount()\nif ll_rowCount &lt; 1 then return ll_rowCount\n\n\/\/ count matching rows\nll_row = idw_requestor.find(as_expression, 1, ll_rowCount)\ndo while ll_row &gt; 0 and ll_row &lt;= ll_rowCount\n\tll_numKeys++\n\tll_row++\n\tll_row = idw_requestor.find(as_expression, ll_row, ll_rowCount)\nloop\n\n\/\/ get out if no matches\nif ll_numKeys &lt; 1 then return ll_numKeys\n\n\/\/ get block of keys\nll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)\n\n\/\/ apply keys to the only matching rows\nll_row = idw_requestor.find(as_expression, 1, ll_rowCount)\ndo while ll_row &gt; 0 and ll_row &lt;= ll_rowCount\n\n\t\/\/ remember row status\n\tif ab_rememberRowStatus then\n\t\tle_rowStatus = idw_Requestor.getItemStatus(ll_row, 0, primary!)\n\tend if\n\n\t\/\/ set the column with the next key\n\tidw_requestor.setItem(ll_row, as_columnName, ll_nextKey)\n\t\n\t\/\/ reset row status to the remembered row status\n\tif ab_rememberRowStatus then\n\t\t\n\t\t\/\/ if the row status was originally new! or notModified!, then changing\n\t\t\/\/ it to notModified! will reset the row status. if the row status\n\t\t\/\/ was either newModified! or dataModified!, then the row should\n\t\t\/\/ already have its original row status.\n\t\t\n\t\tif le_rowStatus = new! or le_rowStatus = notModified! then\n\t\t\tidw_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)\n\t\tend if\n\t\t\n\tend if\n\n\tll_nextKey++\n\tll_row++\n\t\n\tll_row = idw_requestor.find(as_expression, ll_row, ll_rowCount)\n\t\nloop\n\t\nreturn 1\n\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname);\n\/\/ \n\/\/ pass on call using defaults\nreturn this.of_getNextKey(as_tableName, as_columnName, true)\nend function\n\npublic function integer of_register ();\n\/\/ \n\/\/ automatic registering of tables and columns with the next key service\n\/\/ this method looks for objects on a dataobject with a tag of &quot;nextkey=table&quot;\nn_cst_string lnv_string\nstring ls_objects[], ls_tag, ls_tableName, ls_columnName\ninteger li_objectCount, li_objectPointer\n\n\/\/ make sure base datawindow service is started\nif not isValid (idw_requestor.inv_base) then\n\tidw_requestor.of_setBase(true)\nend if\n\n\/\/ get dataobject objects into an array\nli_objectCount = idw_requestor.inv_base.of_getObjects (ls_objects[])\n\n\/\/ loop through objects\nfor li_objectPointer = 1 to li_objectCount\n\t\n\t\/\/ get object name\n\tls_columnName = ls_objects[li_objectPointer]\n\t\n\t\/\/ get tag for object\n\tls_tag = idw_requestor.describe(ls_columnName + &quot;.tag&quot;)\n\n\t\/\/ get &quot;nextkey&quot; key.\n\tif not isNull(ls_tag) and len(trim(ls_tag)) &gt; 0 then\n\t\tls_tableName = lnv_string.of_getKeyValue (ls_tag, &quot;nextkey&quot;, &quot;|&quot;)\n\tend if\n\t\n\t\/\/ register table\/column with the next key service\n\tif not isNull(ls_tableName) and len(trim(ls_tableName)) &gt; 0 then\n\t\tthis.of_register(ls_tableName, ls_columnName)\n\tend if\n\t\nnext\n\nreturn 1\nend function\n\non n_cst_dwsrv_nextkey.create\ncall super::create\nend on\n\non n_cst_dwsrv_nextkey.destroy\ncall super::destroy\nend on\n\nevent constructor;call super::constructor;\n\/\/ \ninv_nextKey = create n_cst_nextKey\nend event\n\nevent destructor;call super::destructor;\n\/\/ \nif isValid(inv_nextKey) then destroy(inv_nextKey)\nend event<\/pre>\n<p><strong>Export of n_cst_dssrv_nextkey object for Datastores<\/strong><\/p>\n<pre name=\"code\" class=\"VB\">$PBExportHeader$n_cst_dssrv_nextkey.sru\n$PBExportComments$Next Key DataWindow Service\nforward\nglobal type n_cst_dssrv_nextkey from n_cst_dssrv\nend type\nend forward\n\nglobal type n_cst_dssrv_nextkey from n_cst_dssrv\nend type\nglobal n_cst_dssrv_nextkey n_cst_dssrv_nextkey\n\ntype variables\n\nprotected:\nn_cst_nextKey inv_nextKey\nn_tr itr_object\nend variables\n\nforward prototypes\npublic function integer of_settransobject (n_tr atr_object)\npublic function integer of_getnextkey (string as_tablename, string as_columnname)\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression)\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_resetrowstatus)\npublic function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_resetrowstatus)\nend prototypes\n\npublic function integer of_settransobject (n_tr atr_object);\n\/\/ \ninteger li_rc\n\n\/\/ validate\nif IsNull (atr_object) or not IsValid (atr_object) then return FAILURE\n\n\/\/ set the trans object\nli_rc = inv_nextKey.of_setTransObject (atr_object)\n\n\/\/ remember the trans object\nif li_rc = 1 then\n\titr_object = atr_object\nend if\n\nreturn li_rc\n\n\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname);\n\/\/ \n\/\/ pass on call with defaults\nreturn this.of_getNextKey(as_tableName, as_columnName, true)\n\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression);\n\/\/ \n\/\/ pass on call with defaults\nreturn this.of_getNextKey(as_tableName, as_columnName, as_expression, true)\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_resetrowstatus);\n\/\/ \n\/\/ apply keys to a column for selected rows in a datawindow based\n\/\/ based on an expression\nlong ll_row, ll_rowCount, ll_numKeys, ll_nextKey\ndwItemStatus le_rowStatus\n\n\/\/ get total number of rows\nll_rowCount = ids_requestor.rowCount()\nif ll_rowCount &lt; 1 then return ll_rowCount\n\n\/\/ count matching rows\nll_row = ids_requestor.find(as_expression, 1, ll_rowCount)\ndo while ll_row &gt; 0 and ll_row &lt;= ll_rowCount\n\tll_numKeys++\n\tll_row++\n\tll_row = ids_requestor.find(as_expression, ll_row, ll_rowCount)\nloop\n\n\/\/ get out if no matches\nif ll_numKeys &lt; 1 then return ll_numKeys\n\n\/\/ get block of keys\nll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)\n\n\/\/ apply keys to the only matching rows\nll_row = ids_requestor.find(as_expression, 1, ll_rowCount)\ndo while ll_row &gt; 0 and ll_row &lt;= ll_rowCount\n\n\t\/\/ remember row status\n\tif ab_resetRowStatus then\n\t\tle_rowStatus = ids_Requestor.getItemStatus(ll_row, 0, primary!)\n\tend if\n\n\t\/\/ set the column with the next key\n\tids_requestor.setItem(ll_row, as_columnName, ll_nextKey)\n\t\n\t\/\/ reset row status\n\tif ab_resetRowStatus then\n\t\t\n\t\t\/\/ if the row status was originally new! or notModified!, then changing\n\t\t\/\/ it to notModified! will reset the row status. if the row status\n\t\t\/\/ was either newModified! or dataModified!, then the row should\n\t\t\/\/ already have its original row status.\n\t\t\n\t\tif le_rowStatus = new! or le_rowStatus = notModified! then\n\t\t\tids_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)\n\t\tend if\n\t\t\n\tend if\n\n\tll_nextKey++\n\tll_row++\n\t\n\tll_row = ids_requestor.find(as_expression, ll_row, ll_rowCount)\n\t\nloop\n\t\nreturn 1\n\nend function\n\npublic function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_resetrowstatus);\n\/\/ \n\/\/ apply keys to a column for all rows in this datawindow\nlong ll_row, ll_rowCount, ll_numKeys, ll_nextKey\ndwItemStatus le_rowStatus\n\n\/\/ get total number of rows\nll_rowCount = ids_requestor.rowCount()\nif ll_rowCount &lt; 1 then return ll_rowCount\n\n\/\/ number of keys = all rows\t\nll_numKeys = ll_rowCount\n\t\n\/\/ get block of keys\nll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)\n\n\/\/ apply keys to all rows\nll_row = 1\ndo while ll_row &gt; 0 and ll_row &lt;= ll_rowCount\n\t\n\t\/\/ remember row status\n\tif ab_resetRowStatus then\n\t\tle_rowStatus = ids_Requestor.getItemStatus(ll_row, 0, primary!)\n\tend if\n\n\t\/\/ set the column with the next key\n\tids_requestor.setItem(ll_row, as_columnName, ll_nextKey)\n\t\n\t\/\/ reset row status\n\tif ab_resetRowStatus then\n\t\t\n\t\t\/\/ if the row status was originally new! or notModified!, then changing\n\t\t\/\/ it to notModified! will reset the row status. if the row status\n\t\t\/\/ was either newModified! or dataModified!, then the row should\n\t\t\/\/ already have its original row status.\n\t\t\n\t\tif le_rowStatus = new! or le_rowStatus = notModified! then\n\t\t\tids_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)\n\t\tend if\n\t\t\n\tend if\n\t\n\tll_nextKey++\n\tll_row++\n\t\nloop\n\nreturn 1\n\nend function\n\nevent constructor;call super::constructor;\n\/\/ \ninv_nextKey = create n_cst_nextKey\nend event\n\non n_cst_dssrv_nextkey.create\ncall super::create\nend on\n\non n_cst_dssrv_nextkey.destroy\ncall super::destroy\nend on\n\nevent destructor;call super::destructor;\n\/\/ \nif isValid(inv_nextKey) then destroy(inv_nextKey)\nend event<\/pre>\n<p><strong>Datawindow Object Database Information from export<\/strong><\/p>\n<pre name=\"code\" class=\"VB\">table(column=(type=decimal(0) updatewhereclause=yes name=nextkey dbname=&quot;nextKey&quot; )\n procedure=&quot;1 execute dbo.spcmn_getNextKeyResultSet;1 @isTableName = :isTableName, @iiKeys = :iiKeys&quot; arguments=((&quot;isTableName&quot;, string),(&quot;iiKeys&quot;, number)) )<\/pre>\n<p>Once you have the stored procedures in your database, create a datawindow object with the spcmn_getNextKeyResultSet procedure as its datasource.<\/p>\n<p><strong>Stored Procedure for the NextKey datawindow object<\/strong><\/p>\n<p>This calls the spcmn_getNextKey procedure detailed in the earlier post.<\/p>\n<pre name=\"code\" class=\"VB\">\/****** Object: Procedure [dbo].[spcmn_getNextKeyResultSet]   Script Date: 12\/30\/2010 10:17:41 AM ******\/\nIF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N&#039;[dbo].[spcmn_getNextKeyResultSet]&#039;) AND type in (N&#039;P&#039;, N&#039;PC&#039;))\nBEGIN\nDROP PROCEDURE [dbo].[spcmn_getNextKeyResultSet];\nEND\nGO\n\nSET ANSI_NULLS ON;\nGO\nSET QUOTED_IDENTIFIER OFF;\nGO\n\nCREATE PROCEDURE [dbo].[spcmn_getNextKeyResultSet]\n    @isTableName varchar(128)  = null -- table to get next key for\n   ,@iiKeys      decimal(18,0) = null -- number of keys to get; null = 1\nAS\n   set nocount on\n   -- ----------------------------------------------------------\n   -- Procedure: spcmn_getNextKeyResultSet\n   -- Get next technical key(s) for the table name passed in.\n   -- ----------------------------------------------------------\n\n   declare @lsERRORTEXT varchar(255)\n          ,@liERROR     int\n          ,@liROWCOUNT  int\n          ,@liTRANCOUNT int\n\n   declare @liNextKey decimal(18,0)\n\n   -- work around for the PowerBuilder IDE\n   if @iiKeys = 0\n   begin\n      set @liNextKey = 0\n      select @liNextKey &#039;nextKey&#039;   \n      return\n   end\n   -- initialize error handling\n   select @liERROR = @@ERROR\n   if @liERROR &lt;&gt; 0 goto ErrorHandler\n\n   -- get next key\n   exec spcmn_getNextKey @isTableName, @liNextKey output, @iiKeys\n\n   select @liERROR = @@ERROR\n   IF @liERROR &lt;&gt; 0 goto ErrorHandler\n\n   select @liNextKey &#039;nextKey&#039;\n\n   return\n\nErrorHandler:\n   if @@TRANCOUNT &gt; @liTRANCOUNT --0\n      rollback transaction\n\n   select @lsERRORTEXT = isNull(@lsERRORTEXT, &#039;spcmn_getNextKeyResultSet: Unexpected Error Occurred!&#039;)\n   raiserror(@lsERRORTEXT, 0, 1) WITH SETERROR\n\nGO\nGRANT EXECUTE ON [dbo].[spcmn_getNextKeyResultSet] TO [public];\nGO<\/pre>\n<p><strong>Datastore (n_ds) object export<\/strong><\/p>\n<pre name=\"code\" class=\"VB\">$PBExportHeader$n_ds.sru\n$PBExportComments$Extension Datastore class\nforward\nglobal type n_ds from pfc_n_ds\nend type\nend forward\n\nglobal type n_ds from pfc_n_ds\nend type\nglobal n_ds n_ds\n\ntype variables\n\npublic:\nn_cst_dssrv_nextKey inv_nextKey\nend variables\n\nforward prototypes\npublic function integer of_setnextkey (boolean ab_switch)\nend prototypes\n\npublic function integer of_setnextkey (boolean ab_switch);\n\/\/Check arguments\nIf IsNull(ab_switch) Then\n\tReturn -1\nEnd If\n\nIF ab_Switch THEN\n\tIF IsNull(inv_nextKey) Or Not IsValid (inv_nextKey) THEN\n\t\tinv_nextKey = Create n_cst_dssrv_nextKey\n\t\tinv_nextKey.of_setRequestor ( this )\n\t\tReturn 1\n\tEND IF\nELSE \n\tIF IsValid (inv_nextKey) THEN\n\t\tDestroy inv_nextKey\n\t\tReturn 1\n\tEND IF\t\nEND IF\n\nReturn 0\nend function\n\non n_ds.create\ncall super::create\nend on\n\non n_ds.destroy\ncall super::destroy\nend on<\/pre>\n<p><strong>Datawindow (u_dw) object export<\/strong><\/p>\n<p>Nextkey service used in pfc_addrow and pfc_insertrow events<\/p>\n<pre name=\"code\" class=\"VB\">$PBExportHeader$u_dw.sru\n$PBExportComments$Extension DataWindow class\nforward\nglobal type u_dw from pfc_u_dw\nend type\nend forward\n\nglobal type u_dw from pfc_u_dw\nend type\nglobal u_dw u_dw\n\ntype variables\n\npublic:\nn_cst_dwsrv_nextKey inv_nextKey\nend variables\n\nforward prototypes\npublic function integer of_setnextkey (boolean ab_switch)\nend prototypes\n\npublic function integer of_setnextkey (boolean ab_switch);\nif IsNull(ab_switch) then return FAILURE\n\nif ab_Switch then\n\tif IsNull(inv_nextKey) or not IsValid (inv_nextKey) then\n\t\tinv_nextKey = Create n_cst_dwsrv_nextKey\n\t\tinv_nextKey.of_SetRequestor (this)\n\t\treturn SUCCESS\n\tend if\nelse \n\tif IsValid (inv_nextKey) then\n\t\tDestroy inv_nextKey\n\t\treturn SUCCESS\n\tend if\t\nend if\n\nreturn NO_ACTION\nend function\n\n\non u_dw.create\ncall super::create\nend on\n\non u_dw.destroy\ncall super::destroy\nend on\n\nevent destructor;call super::destructor;\nof_setNextKey(false)\nend event\n\nevent pfc_addrow;\/\/ override of pfc event!\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/\tEvent:\t\t\tpfc_addrow\n\/\/\tArguments:\t\tNone\n\/\/\tReturns:\t\t\tlong - number of the new row that was inserted\n\/\/\t \t\t\t\t\t0 = No row was added.\n\/\/\t\t\t\t\t\t-1 = error\n\/\/\tDescription:\tAdds a new row to the end of the DW\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nlong\tll_rc\nboolean lb_disablelinkage\n\n\/\/ Allow for pre functionality.\nif this.Event pfc_preinsertrow() &lt;= 0 then return NO_ACTION\n\n\/\/ Is Querymode enabled?\nif IsValid(inv_QueryMode) then lb_disablelinkage = inv_QueryMode.of_GetEnabled()\n\nif not lb_disablelinkage then\n\t\/\/ Notify that a new row is about to be added.\n\tif IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (0) \nend if\n\n\/\/ Insert row.\nif IsValid (inv_RowManager) then\n\tll_rc = inv_RowManager.event pfc_addrow ()\nelse\n\tll_rc = this.InsertRow (0) \nend if\n\n\/\/ pass on event to the next key service\n\/\/ added here so linkage service can know about keys\nif isValid (inv_nextKey) then\tinv_nextKey.event ue_insertRow (ll_rc) \n\nif not lb_disablelinkage then\n\t\/\/ Notify that a new row has been added.\n\tif IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (ll_rc) \nend if\n\n\/\/ Allow for post functionality.\nthis.Post Event pfc_postinsertrow(ll_rc)\n\nreturn ll_rc\nend event\n\nevent pfc_insertrow;\/\/ -- override of pfc event\n\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/\tEvent:\t\t\tpfc_insertrow\n\/\/\tArguments:\t\tNone\n\/\/\tReturns:\t\t\tlong - number of the new row that was inserted\n\/\/\t \t\t\t\t\t0 = No row was added.\n\/\/\t\t\t\t\t\t-1 = error\n\/\/\tDescription:\tInserts a new row into the DataWindow before the current row\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\nlong\tll_currow\nlong\tll_rc\nboolean lb_disablelinkage\n\n\/\/ Allow for pre functionality.\nif this.Event pfc_preinsertrow() &lt;= PREVENT_ACTION then return NO_ACTION\n\n\/\/ Get current row\nll_currow = this.GetRow()\nif ll_currow &lt; 0 then ll_currow = 0\n\n\/\/ Is Querymode enabled?\nif IsValid(inv_QueryMode) then lb_disablelinkage = inv_QueryMode.of_GetEnabled()\n\t\t\nif not lb_disablelinkage then\t\t\n\t\/\/ Notify that a new row is about to be added.\n\tif IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (0) \nend if\n\n\/\/ Insert row.\nif IsValid (inv_RowManager) then\n\tll_rc = inv_RowManager.event pfc_insertrow (ll_currow)\nelse\n\tll_rc = this.InsertRow (ll_currow) \nend if\n\n\/\/ pass on event to the next key service\n\/\/ added here so linkage service can know about keys\nif isValid (inv_nextKey) then\tinv_nextKey.event ue_insertRow (ll_rc) \n\nif not lb_disablelinkage then\t\t\n\t\/\/ Notify that a new row has been added.\n\tif IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (ll_rc) \nend if\n\/\/ Allow for post functionality.\nthis.Post Event pfc_postinsertrow(ll_rc)\n\nreturn ll_rc\nend event<\/pre>\n<p>Special thanks to A.J. Schroeder.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As an extension of the next key stored procedure article, here is a pretty easy to implement a next surrogate key service to use with the PFC. If you don&#8217;t use the PFC it is still fairly easy to use with some modification. You can use this service anytime you want to get a sequential&hellip;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[8,3,10],"tags":[33,12,16,15],"_links":{"self":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/667"}],"collection":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/comments?post=667"}],"version-history":[{"count":8,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/667\/revisions"}],"predecessor-version":[{"id":1913,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/667\/revisions\/1913"}],"wp:attachment":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/media?parent=667"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/categories?post=667"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/tags?post=667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}