Skip to content

Commit f0d83d5

Browse files
larshpCopilot
andauthored
first draft (#2)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 48b9ea2 commit f0d83d5

17 files changed

+654
-22
lines changed

.github/workflows/test.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: test
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
unit:
14+
runs-on: ubuntu-latest
15+
timeout-minutes: 5
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-node@v4
19+
- run: npm run docker:start
20+
- run: npm install
21+
- run: npm test

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
output
2+
node_modules
3+
package-lock.json
4+
output_test

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ignore-scripts=true

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,22 @@
11
# open-abap-lock
2-
lock
2+
3+
Concurrent/cross session locking for Open ABAP.
4+
5+
Requires and works with PostgreSQL as the database backend, and only on the DEFAULT connection.
6+
7+
## Notes
8+
9+
* [PostgreSQL - Advisory Locks](https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS)
10+
* [PostgreSQL - Advisory Lock Functions](https://www.postgresql.org/docs/9.1/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS)
11+
12+
wildcards?
13+
scope?
14+
release on crash
15+
table `pg_locks`
16+
17+
## Todo
18+
19+
* `_scope`
20+
* `wait` flag
21+
* `mode`
22+
* release at commit work if update task

abap_transpile.jsonc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"input_folder": ["src", "test/src"],
3+
"input_filter": [],
4+
"output_folder": "output",
5+
"write_unit_tests": true,
6+
"write_source_map": true,
7+
"libs": [
8+
{
9+
"url": "https://github.com/open-abap/open-abap-core"
10+
}
11+
],
12+
"options": {
13+
"ignoreSyntaxCheck": false,
14+
"addFilenames": true,
15+
"addCommonJS": true,
16+
"populateTables": {
17+
"reposrc": false
18+
},
19+
"setup": {
20+
"filename": "../test/setup.mjs",
21+
"postFunction": "postFunction",
22+
"preFunction": "preFunction"
23+
}
24+
}
25+
}

abaplint.jsonc

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"global": {
3-
"files": "/src/**/*.*"
3+
"files": [
4+
"/src/**/*.*",
5+
"/test/src/**/*.*"
6+
]
47
},
58
"dependencies": [
69
{
@@ -130,7 +133,7 @@
130133
"exportDynpro": true,
131134
"dynamicSQL": true
132135
},
133-
"db_operation_in_loop": true,
136+
"db_operation_in_loop": false,
134137
"definitions_top": true,
135138
"description_empty": false,
136139
"double_space": {
@@ -308,25 +311,9 @@
308311
"checkForms": true
309312
},
310313
"method_overwrites_builtin": true,
311-
"method_parameter_names": {
312-
"exclude": [],
313-
"severity": "Error",
314-
"patternKind": "required",
315-
"ignoreNames": [],
316-
"ignorePatterns": [],
317-
"ignoreExceptions": true,
318-
"importing": "^I._.+$",
319-
"returning": "^R._.+$",
320-
"changing": "^C._.+$",
321-
"exporting": "^E._.+$"
322-
},
314+
"method_parameter_names": false,
323315
"mix_returning": true,
324-
"modify_only_own_db_tables": {
325-
"exclude": [],
326-
"severity": "Error",
327-
"reportDynamic": true,
328-
"ownTables": "^[yz]"
329-
},
316+
"modify_only_own_db_tables": false,
330317
"msag_consistency": {
331318
"exclude": [],
332319
"severity": "Error",
@@ -548,7 +535,7 @@
548535
"unused_types": false,
549536
"unused_variables": false,
550537
"use_bool_expression": true,
551-
"use_class_based_exceptions": true,
538+
"use_class_based_exceptions": false,
552539
"use_line_exists": true,
553540
"use_new": true,
554541
"when_others_last": true,

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "open-abap-lock",
3+
"version": "1.0.0",
4+
"private": true,
5+
"description": "test",
6+
"scripts": {
7+
"lint": "abaplint",
8+
"unit": "rm -rf output && abap_transpile abap_transpile.jsonc && echo RUNNING && node output/index.mjs",
9+
"docker:start": "docker compose -p open-abap-lock -f test/stack.yml up -d",
10+
"docker:stop": "docker compose -p open-abap-lock -f test/stack.yml down -v",
11+
"test": "npm run lint && npm run unit"
12+
},
13+
"license": "",
14+
"dependencies": {
15+
"@abaplint/cli": "^2.114.6",
16+
"@abaplint/runtime": "^2.12.16",
17+
"@abaplint/database-pg": "^2.11.78",
18+
"@abaplint/transpiler-cli": "^2.12.16"
19+
}
20+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,131 @@
11
CLASS kernel_lock_concurrent DEFINITION PUBLIC.
22
PUBLIC SECTION.
3+
CLASS-METHODS class_constructor.
4+
5+
CLASS-METHODS enqueue
6+
IMPORTING
7+
input TYPE any
8+
table_name TYPE string
9+
enqueue_name TYPE string
10+
EXCEPTIONS
11+
foreign_lock
12+
system_failure.
13+
14+
CLASS-METHODS dequeue
15+
IMPORTING
16+
table_name TYPE string
17+
enqueue_name TYPE string
18+
input TYPE any.
19+
20+
TYPES: BEGIN OF ty_cleanup,
21+
valid_locks TYPE i,
22+
cleaned_locks TYPE i,
23+
END OF ty_cleanup.
24+
CLASS-METHODS cleanup_locks
25+
RETURNING
26+
VALUE(rs_result) TYPE ty_cleanup.
27+
PRIVATE SECTION.
28+
CLASS-METHODS build_lock_key
29+
IMPORTING
30+
input TYPE any
31+
table_name TYPE string
32+
RETURNING
33+
VALUE(rv_lock_key) TYPE kernel_locks-lock_key.
334
ENDCLASS.
435

536
CLASS kernel_lock_concurrent IMPLEMENTATION.
637

38+
METHOD class_constructor.
39+
cleanup_locks( ).
40+
ENDMETHOD.
41+
42+
METHOD cleanup_locks.
43+
SELECT * FROM kernel_locks INTO TABLE @DATA(lt_locks) ORDER BY PRIMARY KEY ##SUBRC_OK.
44+
LOOP AT lt_locks INTO DATA(ls_lock).
45+
DATA(lv_exists) = lcl_advisory=>exists( lcl_key=>encode( ls_lock-lock_key ) ).
46+
IF lv_exists = abap_true.
47+
rs_result-valid_locks = rs_result-valid_locks + 1.
48+
ELSE.
49+
DELETE FROM kernel_locks WHERE table_name = @ls_lock-table_name AND lock_key = @ls_lock-lock_key.
50+
rs_result-cleaned_locks = rs_result-cleaned_locks + 1.
51+
ENDIF.
52+
ENDLOOP.
53+
ENDMETHOD.
54+
55+
METHOD build_lock_key.
56+
57+
DATA lr_dref TYPE REF TO data.
58+
DATA lo_structdescr TYPE REF TO cl_abap_structdescr.
59+
DATA lv_string TYPE string.
60+
61+
FIELD-SYMBOLS <lg_row> TYPE any.
62+
63+
CREATE DATA lr_dref TYPE (table_name).
64+
ASSIGN lr_dref->* TO <lg_row>.
65+
66+
lo_structdescr ?= cl_abap_typedescr=>describe_by_data( <lg_row> ).
67+
ASSERT lo_structdescr IS NOT INITIAL.
68+
69+
LOOP AT lo_structdescr->components INTO DATA(ls_component).
70+
WRITE '@KERNEL lv_string.set(input[ls_component.get().name.get().toLowerCase().trimEnd()] || "");'.
71+
72+
ASSIGN COMPONENT ls_component-name OF STRUCTURE <lg_row> TO FIELD-SYMBOL(<lv_row_field>).
73+
ASSERT sy-subrc = 0.
74+
<lv_row_field> = lv_string.
75+
ENDLOOP.
76+
77+
rv_lock_key = <lg_row>.
78+
79+
ENDMETHOD.
80+
81+
METHOD enqueue.
82+
83+
DATA ls_lock_row TYPE kernel_locks.
84+
85+
*******************
86+
87+
DATA(lv_lock_key) = build_lock_key(
88+
input = input
89+
table_name = table_name ).
90+
ASSERT lv_lock_key IS NOT INITIAL.
91+
92+
ls_lock_row-table_name = table_name.
93+
ls_lock_row-lock_key = lv_lock_key.
94+
ls_lock_row-username = sy-uname.
95+
GET TIME STAMP FIELD ls_lock_row-timestamp.
96+
ls_lock_row-hostname = sy-host.
97+
ls_lock_row-lock_mode = ''.
98+
ls_lock_row-lock_name = enqueue_name.
99+
100+
TRY.
101+
lcl_advisory=>lock( lcl_key=>encode( ls_lock_row-lock_key ) ).
102+
CATCH lcx_advisory_lock_failed.
103+
RAISE foreign_lock.
104+
ENDTRY.
105+
106+
INSERT kernel_locks FROM @ls_lock_row.
107+
ASSERT sy-subrc = 0.
108+
109+
ENDMETHOD.
110+
111+
METHOD dequeue.
112+
113+
DATA(lv_lock_key) = build_lock_key(
114+
input = input
115+
table_name = table_name ).
116+
117+
TRY.
118+
lcl_advisory=>lock( lcl_key=>encode( lv_lock_key ) ).
119+
CATCH lcx_advisory_lock_failed.
120+
" it doesnt have the lock, or another session has the lock
121+
RETURN.
122+
ENDTRY.
123+
124+
DELETE FROM kernel_locks WHERE table_name = table_name AND lock_key = lv_lock_key.
125+
126+
" advisory locks stack,
127+
lcl_advisory=>unlock( lcl_key=>encode( lv_lock_key ) ).
128+
lcl_advisory=>unlock( lcl_key=>encode( lv_lock_key ) ).
129+
ENDMETHOD.
130+
7131
ENDCLASS.

0 commit comments

Comments
 (0)