1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-09 18:30:06 +00:00

Compare commits

..

57 Commits

Author SHA1 Message Date
Allan Bowe
a9185a2bf2 Merge pull request #338 from sasjs/issue337
fix: adding support for multilabel and notsorted formats
2023-06-21 18:24:16 +01:00
Allan
f0b77dfc6a chore: removing mp_ds2md dump from mp_loadformat.test.2.sas 2023-06-21 17:02:12 +01:00
Allan
91c4b87496 chore(docs): updating markdown table in mp_loadformat.test.2.sas 2023-06-21 17:00:49 +01:00
Allan
111d0dffc3 chore: removing redundant dependency from header 2023-06-21 16:46:31 +01:00
Allan
4f481ec8b4 fix: adding support for multilabel and notsorted formats
included additional test job covering multiple scenarios.  Closes #337
2023-06-21 16:41:46 +01:00
Allan
b8cec22a88 fix: mp_aligndecimal in wrong dependency section 2023-06-20 17:40:56 +01:00
Allan
6b1accdd6b chore(docs): updating comments 2023-06-20 17:18:53 +01:00
Allan Bowe
949b406c23 Update FUNDING.yml 2023-06-20 11:48:00 +01:00
Allan Bowe
fc90a7f928 Update FUNDING.yml
chore: removing github sponsorship option and replacing with nostr

https://iris.to/npub1sasjs00efhywf9uu754wxcetd32edenrczl274ks7ju2y2yn70sqvzsdhn
2023-06-20 11:36:59 +01:00
Allan Bowe
d0bd88907f Merge pull request #336 from sasjs/issue335
Issue335
2023-06-20 11:01:29 +01:00
4e21772207 ci: sasjs cli fix 2023-06-20 11:39:19 +02:00
Allan
39f700bed2 chore: regenerating all.sas
(also adding reminder in PR template)
2023-06-20 09:46:43 +01:00
Allan
dcb7958950 feat: enabling informats to be ingested with the mp_loadformat macro 2023-06-20 00:21:19 +01:00
Allan
146610b5a7 chore(tests): fixing tests on mp_aligndecimal 2023-06-20 00:19:27 +01:00
Allan
9887efcf60 feat: new mp_aligndecimal macro
Includes a test, and an update to the mp_assertcolvals test to include a new test type (NOVAL)
2023-06-19 23:54:18 +01:00
Allan Bowe
a2c7bdafb4 chore: update yaml for build/deploy/test 2023-06-15 09:13:34 +01:00
Allan Bowe
c58b5c7a52 Merge pull request #332 from sasjs/patches
fix: invalid macro variable in mp_lockanytable.sas
2023-04-07 11:30:04 +01:00
Allan Bowe
22c7e5b4dd Merge branch 'main' into patches 2023-04-07 11:29:30 +01:00
allan
244171f8c4 fix: failing test due to missing sashelp table, also added scope check 2023-04-06 13:41:02 +01:00
Allan Bowe
fd765e2d68 Merge pull request #333 from sasjs/vpn
github action vpn
2023-04-06 12:07:56 +01:00
840cb5ef44 chore: ci - sas9.4gl.io 2023-04-06 11:16:25 +02:00
918ce96fce chore: ci - jammy ubuntu 2023-04-05 23:07:35 +02:00
f1712c34e8 chore: ci - jammy ubuntu 2023-04-05 22:58:48 +02:00
11ec20b472 chore: ci - jammy ubuntu 2023-04-05 22:54:50 +02:00
f42f111462 chore: ci - jammy ubuntu 2023-04-05 22:49:45 +02:00
907725c5ba chore: ci - jammy ubuntu 2023-04-05 22:48:09 +02:00
95b78b91e1 chore: ci release 2023-04-05 22:46:17 +02:00
e4771b9c14 ci: trigger 2023-04-05 22:24:18 +02:00
ba8190883e chore: ci added vpn for sas9 2023-04-05 22:13:13 +02:00
allan
32dd057e83 fix: avoid open file handle when the variable to find is not provided (in mf_existvar)
Includes a test that was failing and is now passing
2023-04-05 15:29:53 +01:00
allan
7471bd42a4 fix: invalid macro variable in mp_lockanytable.sas 2023-04-05 14:55:00 +01:00
munja
702a4ecd3a chore(lint): adding lineendings rule (LF) 2023-03-31 13:58:30 +01:00
Allan Bowe
5da97295ff Merge pull request #330 from sasjs/issue329
fix: closes #329 by handling the case of unlocking a table that was n…
2023-02-16 14:35:29 +00:00
munja
dc556bdef0 fix: closes #329 by handling the case of unlocking a table that was never locked in mp_lockanytable.sas, also created a corresponding test plus an extra test to check for scope leakage. all.sas was regenerated. 2023-02-16 14:33:53 +00:00
Allan Bowe
111731bf35 Merge pull request #328 from sasjs/all-contributors/add-henrik-forsell
docs: add henrik-forsell as a contributor for doc
2023-02-15 09:32:09 +00:00
allcontributors[bot]
2c526cf9dd docs: update .all-contributorsrc [skip ci] 2023-02-15 09:31:52 +00:00
allcontributors[bot]
660e02193f docs: update README.md [skip ci] 2023-02-15 09:31:51 +00:00
Allan Bowe
00b4dee86e Merge pull request #327 from henrik-forsell/doc-fix
Changed documentation wording (Column to Dataset)
2023-02-15 09:28:50 +00:00
Henrik Forsell
3913825c22 Changed documentation wording (Column to Dataset) 2023-02-15 15:57:11 +13:00
Allan Bowe
0f143d603b Merge pull request #326 from sasjs/325-error-the-keyword-parameter-maxobs-was-not-defined-with-the-macro
fix: closes #325 by including maxobs param
2023-02-13 14:05:36 +00:00
munja
f1d5fa2c0a fix: closes #325 by including maxobs param 2023-02-13 14:01:43 +00:00
Allan Bowe
a88689428f fix: updating cycjimmy/semantic-release-action to v3 2023-01-25 12:56:38 +00:00
munja
8843fa8bfc fix: adding nrstr() wrapper in mm_adduser2group 2023-01-25 12:53:10 +00:00
Allan Bowe
22d046cf5c Merge pull request #324 from sasjs/servertestfixes
fix: updating ms_runstp and ms_testservice macros to cater for latest…
2023-01-06 13:15:11 +01:00
munja
29e3eb34aa fix: updating ms_runstp and ms_testservice macros to cater for latest response JSON formats in sasjs/server 2023-01-06 12:13:36 +00:00
munja
1af52a6683 fix: adding des= macro option to mf_abort 2023-01-02 11:26:21 +00:00
munja
fc0c96dd94 chore: merge 2022-12-30 12:41:08 +00:00
munja
b9c4882553 fix: linting issues 2022-12-30 12:38:34 +00:00
Allan Bowe
011b2b185c chore: removing broken badges in README 2022-12-28 20:25:05 +00:00
Allan Bowe
dbc23550ac Merge pull request #323 from sasjs/ms_getgroups
fix: increasing desc length to 256 in ms_getgroups
2022-12-28 21:21:35 +01:00
munja
8910840ccc fix: increasing desc length to 256 in ms_getgroups 2022-12-28 20:17:08 +00:00
Allan Bowe
4ef571032d Merge pull request #322 from sasjs/upds
Upds
2022-12-14 14:21:20 +01:00
Allan Bowe
e01cd8cd16 Merge branch 'main' into upds 2022-12-14 14:20:51 +01:00
munja
00628ec78a chore: updating lint settings, some line ending issues, and a sasjsconfig apploc fix 2022-12-14 14:19:28 +01:00
munja
f4e6a487f3 fix: removing redundant param in mS/M_webout macros 2022-12-14 14:17:06 +01:00
Allan Bowe
b7afecdf81 fix: escaping syswarningtext and syserrortext in mp_abort 2022-12-04 21:14:10 +00:00
Allan Bowe
19eb348f0e fix: else case for issue #320 2022-12-04 18:59:31 +00:00
50 changed files with 2201 additions and 948 deletions

View File

@@ -135,6 +135,15 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "henrik-forsell",
"name": "Henrik Forsell",
"avatar_url": "https://avatars.githubusercontent.com/u/109935936?v=4",
"profile": "https://github.com/henrik-forsell",
"contributions": [
"doc"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [sasjs] custom: https://getalby.com/p/sasjs

View File

@@ -15,3 +15,4 @@ What code changes have been made to achieve the intent.
- [ ] Code is formatted correctly (`sasjs lint`). - [ ] Code is formatted correctly (`sasjs lint`).
- [ ] Any new functionality has been unit tested. - [ ] Any new functionality has been unit tested.
- [ ] All unit tests are passing (`sasjs test`). - [ ] All unit tests are passing (`sasjs test`).
- [ ] `all.sas` has been regenerated (`python3 build.py`)

25
.github/vpn/config.ovpn vendored Normal file
View File

@@ -0,0 +1,25 @@
# Client
client
tls-client
dev tun
# this will connect with whatever proto DNS tells us (https://community.openvpn.net/openvpn/ticket/934)
proto tcp
remote vpn.4gl.io 7494
resolv-retry infinite
cipher AES-256-CBC
auth SHA256
script-security 2
keepalive 10 120
remote-cert-tls server
# Keys
ca ca.crt
cert user.crt
key user.key
tls-auth tls.key 1
# Security
nobind
persist-key
persist-tun
verb 3

View File

@@ -15,7 +15,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Semantic Release - name: Semantic Release
uses: cycjimmy/semantic-release-action@v2 uses: cycjimmy/semantic-release-action@v3
env: env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -21,32 +21,49 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Write VPN Files
run: |
echo "$CA_CRT" > .github/vpn/ca.crt
echo "$USER_CRT" > .github/vpn/user.crt
echo "$USER_KEY" > .github/vpn/user.key
echo "$TLS_KEY" > .github/vpn/tls.key
shell: bash
env:
CA_CRT: ${{ secrets.CA_CRT}}
USER_CRT: ${{ secrets.USER_CRT }}
USER_KEY: ${{ secrets.USER_KEY }}
TLS_KEY: ${{ secrets.TLS_KEY }}
- name: Install Open VPN
run: |
sudo apt install apt-transport-https
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
sudo apt-key add openvpn-repo-pkg-key.pub
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-jammy.list
sudo apt update
sudo apt install openvpn3=17~betaUb22042+jammy
- name: Start Open VPN 3
run: openvpn3 session-start --config .github/vpn/config.ovpn
- name: Install Doxygen - name: Install Doxygen
run: sudo apt-get install doxygen run: sudo apt-get install doxygen
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Check code style - name: Check code style (aborts if errors found)
run: npm run lint run: npx sasjs lint
- name: Add client - name: Add client
run: echo "CLIENT=${{secrets.CLIENT}}"> .env.viya run: echo "CLIENT=${{secrets.SAS9_4GL_IO_CLIENT}}"> .env.server
- name: Add secret
run: echo "SECRET=${{secrets.SECRET}}" >> .env.viya
- name: Add access token - name: Add access token
run: echo "ACCESS_TOKEN=${{secrets.ACCESS_TOKEN}}" >> .env.viya run: echo "ACCESS_TOKEN=${{secrets.SAS9_4GL_IO_ACCESS_TOKEN}}" >> .env.server
- name: Add refresh token - name: Build & Deploy Project to SAS server
run: echo "REFRESH_TOKEN=${{secrets.REFRESH_TOKEN}}" >> .env.viya run: npx sasjs cbd -t server
- name: Build Project - name: Run all tests
run: npm run build run: npx sasjs test -t server
- name: Run SASjs tests
run: npm run test
env: env:
CI: true CI: true
CLIENT: ${{secrets.CLIENT}} CLIENT: ${{secrets.CLIENT}}

View File

@@ -1,13 +1,15 @@
{ {
"noTrailingSpaces": true, "noTrailingSpaces": true,
"noEncodedPasswords": true, "noEncodedPasswords": true,
"hasDoxygenHeader": true, "hasDoxygenHeader": true,
"hasMacroNameInMend": true, "hasMacroNameInMend": true,
"hasMacroParentheses": true, "hasMacroParentheses": true,
"noNestedMacros": false, "lineEndings": "lf",
"noSpacesInFileNames": true, "noGremlins": true,
"maxLineLength": 300, "noNestedMacros": false,
"lowerCaseFileNames": true, "noSpacesInFileNames": true,
"noTabIndentation": true, "maxLineLength": 300,
"indentationMultiple": 2 "lowerCaseFileNames": true,
} "noTabs": true,
"indentationMultiple": 2
}

View File

@@ -6,5 +6,7 @@
"editor.rulers": [ "editor.rulers": [
80 80
], ],
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true,
"sasjs-for-vscode.target": "docsonly",
"sasjs-for-vscode.isLocal": true
} }

View File

@@ -2,8 +2,6 @@
[![npm package][npm-image]][npm-url] [![npm package][npm-image]][npm-url]
[![Github Workflow][githubworkflow-image]][githubworkflow-url] [![Github Workflow][githubworkflow-image]][githubworkflow-url]
![npm](https://img.shields.io/npm/dt/@sasjs/core) ![npm](https://img.shields.io/npm/dt/@sasjs/core)
![Snyk Vulnerabilities for npm package](https://img.shields.io/snyk/vulnerabilities/npm/@sasjs/core)
[![License](https://img.shields.io/apm/l/atomic-design-ui.svg)](/LICENSE)
![GitHub top language](https://img.shields.io/github/languages/top/sasjs/core) ![GitHub top language](https://img.shields.io/github/languages/top/sasjs/core)
[![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sasjs/core)](https://github.com/sasjs/core/issues?q=is%3Aissue+is%3Aclosed) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sasjs/core)](https://github.com/sasjs/core/issues?q=is%3Aissue+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues-raw/sasjs/core)](https://github.com/sasjs/core/issues) [![GitHub issues](https://img.shields.io/github/issues-raw/sasjs/core)](https://github.com/sasjs/core/issues)
@@ -248,7 +246,7 @@ The following repositories are also worth checking out:
## Contributors ✨ ## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors-) [![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END --> <!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -256,22 +254,25 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
<!-- markdownlint-disable --> <!-- markdownlint-disable -->
<table> <table>
<tr> <tbody>
<td align="center"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="#business-allanbowe" title="Business development">💼</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Code">💻</a> <a href="#content-allanbowe" title="Content">🖋</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Documentation">📖</a> <a href="#infra-allanbowe" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑‍🏫</a> <a href="#question-allanbowe" title="Answering Questions">💬</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Tests">⚠️</a></td> <tr>
<td align="center"><a href="https://github.com/rafgag"><img src="https://avatars.githubusercontent.com/u/69139928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rafgag</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=rafgag" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt="Allan Bowe"/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="#business-allanbowe" title="Business development">💼</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Code">💻</a> <a href="#content-allanbowe" title="Content">🖋</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Documentation">📖</a> <a href="#infra-allanbowe" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑‍🏫</a> <a href="#question-allanbowe" title="Answering Questions">💬</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/tmoody"><img src="https://avatars.githubusercontent.com/u/79837106?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Trevor Moody</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=tmoody" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/rafgag"><img src="https://avatars.githubusercontent.com/u/69139928?v=4?s=100" width="100px;" alt="rafgag"/><br /><sub><b>rafgag</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=rafgag" title="Code">💻</a></td>
<td align="center"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/tmoody"><img src="https://avatars.githubusercontent.com/u/79837106?v=4?s=100" width="100px;" alt="Trevor Moody"/><br /><sub><b>Trevor Moody</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=tmoody" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=saadjutt01" title="Code">💻</a> <a href="#ideas-saadjutt01" title="Ideas, Planning, & Feedback">🤔</a></td> <td align="center" valign="top" width="14.28%"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt="Krishna Acondy"/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
<td align="center"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt="Muhammad Saad "/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=saadjutt01" title="Code">💻</a> <a href="#ideas-saadjutt01" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td> <td align="center" valign="top" width="14.28%"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt="Yury Shkoda"/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
</tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt="Mihajlo Medjedovic"/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<tr> </tr>
<td align="center"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td> <tr>
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt="kkchandok"/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt="Vladislav Parhomchik"/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt="Vignesh T."/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=eltociear" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt="Bart Jablonski"/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td>
</tr> <td align="center" valign="top" width="14.28%"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt="Ikko Ashimine"/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=eltociear" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/henrik-forsell"><img src="https://avatars.githubusercontent.com/u/109935936?v=4?s=100" width="100px;" alt="Henrik Forsell"/><br /><sub><b>Henrik Forsell</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=henrik-forsell" title="Documentation">📖</a></td>
</tr>
</tbody>
</table> </table>
<!-- markdownlint-restore --> <!-- markdownlint-restore -->

439
all.sas
View File

@@ -30,7 +30,7 @@ options noquotelenmax;
**/ **/
%macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1) %macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1)
)/*/STORE SOURCE*/; )/des='ungraceful abort' /*STORE SOURCE*/;
%if not(%eval(%unquote(&iftrue))) %then %return; %if not(%eval(%unquote(&iftrue))) %then %return;
@@ -42,7 +42,8 @@ options noquotelenmax;
%mend mf_abort; %mend mf_abort;
/** @endcond *//** /** @endcond */
/**
@file @file
@brief de-duplicates a macro string @brief de-duplicates a macro string
@details Removes all duplicates from a string of words. A delimeter can be @details Removes all duplicates from a string of words. A delimeter can be
@@ -247,7 +248,8 @@ options noquotelenmax;
0 0
%end; %end;
%mend mf_existfileref;/** %mend mf_existfileref;
/**
@file @file
@brief Checks if a function exists @brief Checks if a function exists
@details Returns 1 if the function exists, else 0. Note that this function @details Returns 1 if the function exists, else 0. Note that this function
@@ -310,13 +312,17 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
%local dsid rc; %local dsid rc;
%let dsid=%sysfunc(open(&libds,is)); %let dsid=%sysfunc(open(&libds,is));
%if &dsid=0 or %length(&var)=0 %then %do; %if &dsid=0 %then %do;
%put %sysfunc(sysmsg()); %put %sysfunc(sysmsg());
0 0
%end;
%else %if %length(&var)=0 %then %do;
0
%let rc=%sysfunc(close(&dsid));
%end; %end;
%else %do; %else %do;
%sysfunc(varnum(&dsid,&var)) %sysfunc(varnum(&dsid,&var))
%let rc=%sysfunc(close(&dsid)); %let rc=%sysfunc(close(&dsid));
%end; %end;
%mend mf_existvar; %mend mf_existvar;
@@ -2542,15 +2548,51 @@ and %superq(SYSPROCESSNAME) ne %str(Compute Server)
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ',"_PROGRAM" : ' _PROGRAM ; put ',"_PROGRAM" : ' _PROGRAM ;
put ",""SYSCC"" : ""&syscc"" "; put ",""SYSCC"" : ""&syscc"" ";
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"'); syserrortext=cats(symget('syserrortext'));
put ",""SYSERRORTEXT"" : " syserrortext; if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
syserrortext='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */
prxchange('s/\x0D/\r/',-1, /* carriage return */
prxchange('s/\x09/\\t/',-1, /* tab */
prxchange('s/\x00/\\u0000/',-1, /* NUL */
prxchange('s/\x0E/\\u000E/',-1, /* SS */
prxchange('s/\x0F/\\u000F/',-1, /* SF */
prxchange('s/\x01/\\u0001/',-1, /* SOH */
prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"';
end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSSCPL"" : ""&sysscpl"" "; put ",""SYSSCPL"" : ""&sysscpl"" ";
put ",""SYSSITE"" : ""&syssite"" "; put ",""SYSSITE"" : ""&syssite"" ";
sysvlong=quote(trim(symget('sysvlong'))); sysvlong=quote(trim(symget('sysvlong')));
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"'); syswarningtext=cats(symget('syswarningtext'));
if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
syswarningtext='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */
prxchange('s/\x0D/\r/',-1, /* carriage return */
prxchange('s/\x09/\\t/',-1, /* tab */
prxchange('s/\x00/\\u0000/',-1, /* NUL */
prxchange('s/\x0E/\\u000E/',-1, /* SS */
prxchange('s/\x0F/\\u000F/',-1, /* SF */
prxchange('s/\x01/\\u0001/',-1, /* SOH */
prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"';
end;
else syswarningtext=cats('"',syswarningtext,'"');
put ",""SYSWARNINGTEXT"" : " syswarningtext; put ",""SYSWARNINGTEXT"" : " syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
put "}" ; put "}" ;
@@ -2616,6 +2658,101 @@ and %superq(SYSPROCESSNAME) ne %str(Compute Server)
%mend mp_abort; %mend mp_abort;
/** @endcond */ /** @endcond */
/**
@file
@brief Apply leading blanks to align numbers vertically in a char variable
@details This is particularly useful when storing numbers (as character) that
need to be sorted.
It works by splitting the number left and right of the decimal place, and
aligning it accordingly. A temporary variable is created as part of this
process (which is automatically dropped)
The macro can be used only in data step, eg as follows:
data _null_;
length myvar $50;
do i=1 to 1000 by 50;
if mod(i,2)=0 then j=ranuni(0)*i*100;
else j=i*100;
%mp_aligndecimal(myvar,width=7)
leading_spaces=length(myvar)-length(cats(myvar));
putlog +leading_spaces myvar;
end;
run;
The generated code will look something like this:
length aligndp4e49996 $7;
if index(myvar,'.') then do;
aligndp4e49996=cats(scan(myvar,1,'.'));
aligndp4e49996=right(aligndp4e49996);
myvar=aligndp4e49996!!'.'!!cats(scan(myvar,2,'.'));
end;
else do;
aligndp4e49996=myvar;
aligndp4e49996=right(aligndp4e49996);
myvar=aligndp4e49996;
end;
drop aligndp4e49996;
Results (myvar variable):
0.7683559324
122.8232796
99419.50552
42938.5143414
763.3799189
15170.606073
15083.285773
85443.198707
2022999.2251
12038.658867
1350582.6734
52777.258221
11723.347628
33101.268376
6181622.8603
7390614.0669
73384.537893
1788362.1016
2774586.2219
7998580.8415
@param var The (data step, character) variable to modify
@param width= (8) The number of characters BEFORE the decimal point
<h4> SAS Macros </h4>
@li mf_getuniquename.sas
<h4> Related Programs </h4>
@li mp_aligndecimal.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_aligndecimal(var,width=8);
%local tmpvar;
%let tmpvar=%mf_getuniquename(prefix=aligndp);
length &tmpvar $&width;
if index(&var,'.') then do;
&tmpvar=cats(scan(&var,1,'.'));
&tmpvar=right(&tmpvar);
&var=&tmpvar!!'.'!!cats(scan(&var,2,'.'));
end;
else do;
&tmpvar=cats(&var);
&tmpvar=right(&tmpvar);
&var=&tmpvar;
end;
drop &tmpvar;
%mend mp_aligndecimal;
/** /**
@file @file
@brief Append (concatenate) two or more files. @brief Append (concatenate) two or more files.
@@ -2876,7 +3013,7 @@ run;
results. If it does not exist, it will be created, with the following format: results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256| |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|---|---|---| |---|---|---|
|User Provided description|PASS|Column &inds contained ALL columns| |User Provided description|PASS|Dataset &inds contained ALL columns|
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@@ -2950,7 +3087,7 @@ run;
results. If it does not exist, it will be created, with the following format: results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256| |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|---|---|---| |---|---|---|
|User Provided description|PASS|Column &inds contained ALL columns| |User Provided description|PASS|Dataset &inds contained ALL columns|
<h4> Related Macros </h4> <h4> Related Macros </h4>
@@ -3095,6 +3232,7 @@ run;
@param [in] test= (ALLVALS) The test to apply. Valid values are: @param [in] test= (ALLVALS) The test to apply. Valid values are:
@li ALLVALS - Test is a PASS if ALL values have a match in checkvals @li ALLVALS - Test is a PASS if ALL values have a match in checkvals
@li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals @li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals
@li NOVAL - Test is a PASS if there are NO matches in checkvals
@param [out] outds= (work.test_results) The output dataset to contain the @param [out] outds= (work.test_results) The output dataset to contain the
results. If it does not exist, it will be created, with the following format: results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256| |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
@@ -3150,7 +3288,7 @@ run;
%let test=%upcase(&test); %let test=%upcase(&test);
%if &test ne ALLVALS and &test ne ANYVAL %then %do; %if &test ne ALLVALS and &test ne ANYVAL and &test ne NOVAL %then %do;
%mp_abort( %mp_abort(
mac=&sysmacroname, mac=&sysmacroname,
msg=%str(Invalid test - &test) msg=%str(Invalid test - &test)
@@ -3161,12 +3299,12 @@ run;
%let result=-1; %let result=-1;
%let orig=-1; %let orig=-1;
proc sql noprint; proc sql noprint;
select count(*) into: result select count(*) into: result trimmed
from &lib..&ds from &lib..&ds
where &col not in ( where &col not in (
select &ccol from &clib..&cds select &ccol from &clib..&cds
); );
select count(*) into: orig from &lib..&ds; select count(*) into: orig trimmed from &lib..&ds;
quit; quit;
%local notfound tmp1 tmp2; %local notfound tmp1 tmp2;
@@ -3198,7 +3336,7 @@ run;
length test_description $256 test_result $4 test_comments $256; length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc'); test_description=symget('desc');
test_result='FAIL'; test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result values " test_comments="&sysmacroname: &lib..&ds..&col has &result/&orig values "
!!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound'); !!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
%if &test=ANYVAL %then %do; %if &test=ANYVAL %then %do;
if &result < &orig then test_result='PASS'; if &result < &orig then test_result='PASS';
@@ -3206,6 +3344,9 @@ run;
%else %if &test=ALLVALS %then %do; %else %if &test=ALLVALS %then %do;
if &result=0 then test_result='PASS'; if &result=0 then test_result='PASS';
%end; %end;
%else %if &test=NOVAL %then %do;
if &result=&orig then test_result='PASS';
%end;
%else %do; %else %do;
test_comments="&sysmacroname: Unsatisfied test condition - &test"; test_comments="&sysmacroname: Unsatisfied test condition - &test";
%end; %end;
@@ -3978,6 +4119,7 @@ run;
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas @li mddl_sas_cntlout.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mp_aligndecimal.sas
<h4> Related Macros </h4> <h4> Related Macros </h4>
@li mf_getvarformat.sas @li mf_getvarformat.sas
@@ -4019,16 +4161,24 @@ proc format lib=&libcat cntlout=&cntlds;
%end; %end;
run; run;
data &cntlout; data &cntlout/nonote2err;
if 0 then set &ddlds; if 0 then set &ddlds;
set &cntlds; set &cntlds;
if type="N" then do; by type fmtname notsorted;
start=cats(start);
end=cats(end); /* align the numeric values to avoid overlapping ranges */
if type in ("I","N") then do;
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
end; end;
/* create row marker. Data cannot be sorted without it! */
if first.fmtname then fmtrow=0;
fmtrow+1;
run; run;
proc sort; proc sort;
by fmtname start; by type fmtname fmtrow;
run; run;
proc sql; proc sql;
@@ -5872,6 +6022,7 @@ run;
options ps=max lrecl=max; options ps=max lrecl=max;
data _null_; data _null_;
infile &outref; infile &outref;
if _n_=1 then putlog "# &libds" /;
input; input;
putlog _infile_; putlog _infile_;
run; run;
@@ -9977,10 +10128,10 @@ select distinct lowcase(memname)
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs @param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_nobs.sas @li mf_nobs.sas
@li mp_abort.sas @li mp_abort.sas
@li mp_aligndecimal.sas
@li mp_cntlout.sas @li mp_cntlout.sas
@li mp_lockanytable.sas @li mp_lockanytable.sas
@li mp_storediffs.sas @li mp_storediffs.sas
@@ -9988,7 +10139,8 @@ select distinct lowcase(memname)
<h4> Related Macros </h4> <h4> Related Macros </h4>
@li mddl_dc_difftable.sas @li mddl_dc_difftable.sas
@li mddl_dc_locktable.sas @li mddl_dc_locktable.sas
@li mp_loadformat.test.sas @li mp_loadformat.test.1.sas
@li mp_loadformat.test.2.sas
@li mp_lockanytable.sas @li mp_lockanytable.sas
@li mp_stackdiffs.sas @li mp_stackdiffs.sas
@@ -10021,13 +10173,6 @@ select distinct lowcase(memname)
%let &var=%upcase(&prefix._&var); %let &var=%upcase(&prefix._&var);
%end; %end;
/*
format values can be up to 32767 wide. SQL joins on such a wide column can
cause buffer issues. Update ibufsize and reset at the end.
*/
%let ibufsize=%sysfunc(getoption(ibufsize));
options ibufsize=32767 ;
/* in DC, format catalogs maybe specified in the libds with a -FC extension */ /* in DC, format catalogs maybe specified in the libds with a -FC extension */
%let libcat=%scan(&libcat,1,-); %let libcat=%scan(&libcat,1,-);
@@ -10077,29 +10222,62 @@ run;
* First, extract only relevant formats from the catalog * First, extract only relevant formats from the catalog
*/ */
proc sql noprint; proc sql noprint;
select distinct upcase(fmtname) into: fmtlist separated by ' ' from &libds; select distinct
case
when type='N' then upcase(fmtname)
when type='C' then cats('$',upcase(fmtname))
when type='I' then cats('@',upcase(fmtname))
when type='J' then cats('@$',upcase(fmtname))
else "&sysmacroname:UNHANDLED"
end
into: fmtlist separated by ' '
from &libds;
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts) %mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
/* get a hash of the row */
%local cvars nvars;
%let cvars=TYPE FMTNAME START END LABEL PREFIX FILL SEXCL EEXCL HLO DECSEP
DIG3SEP DATATYPE LANGUAGE;
%let nvars=FMTROW MIN MAX DEFAULT LENGTH FUZZ MULT NOEDIT;
data &base_fmts/note2err;
set &base_fmts;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/** /**
* Ensure input table and base_formats have consistent lengths and types * Ensure input table and base_formats have consistent lengths and types
*/ */
%mddl_sas_cntlout(libds=&template) data &inlibds/nonote2err;
data &inlibds; length &delete_col $3 FMTROW 8 start end label $32767;
length &delete_col $3; if 0 then set &base_fmts;
if 0 then set &template;
set &libds; set &libds;
by type fmtname notsorted;
if &delete_col='' then &delete_col='No'; if &delete_col='' then &delete_col='No';
fmtname=upcase(fmtname); fmtname=upcase(fmtname);
type=upcase(type);
if missing(type) then do; if missing(type) then do;
if substr(fmtname,1,1)='$' then type='C'; if substr(fmtname,1,1)='@' then do;
else type='N'; if substr(fmtname,2,1)='$' then type='J';
else type='I';
end;
else do;
if substr(fmtname,1,1)='$' then type='C';
else type='N';
end;
end; end;
if type='N' then do; if type in ('N','I') then do;
start=cats(start); %mp_aligndecimal(start,width=16)
end=cats(end); %mp_aligndecimal(end,width=16)
end; end;
/* update row marker - retain new var as fmtrow may already be in libds */
if first.fmtname then row=1;
else row+1;
drop row;
fmtrow=row;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run; run;
/** /**
@@ -10110,11 +10288,10 @@ create table &outds_add(drop=&delete_col) as
select a.* select a.*
from &inlibds a from &inlibds a
left join &base_fmts b left join &base_fmts b
on a.fmtname=b.fmtname on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
and a.start=b.start
where b.fmtname is null where b.fmtname is null
and upcase(a.&delete_col) ne "YES" and upcase(a.&delete_col) ne "YES"
order by fmtname, start;; order by type, fmtname, fmtrow;
/** /**
* Identify deleted records * Identify deleted records
@@ -10123,10 +10300,9 @@ create table &outds_del(drop=&delete_col) as
select a.* select a.*
from &inlibds a from &inlibds a
inner join &base_fmts b inner join &base_fmts b
on a.fmtname=b.fmtname on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
and a.start=b.start
where upcase(a.&delete_col)="YES" where upcase(a.&delete_col)="YES"
order by fmtname, start; order by type, fmtname, fmtrow;
/** /**
* Identify modified records * Identify modified records
@@ -10135,12 +10311,10 @@ create table &outds_mod (drop=&delete_col) as
select a.* select a.*
from &inlibds a from &inlibds a
inner join &base_fmts b inner join &base_fmts b
on a.fmtname=b.fmtname on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
and a.start=b.start
where upcase(a.&delete_col) ne "YES" where upcase(a.&delete_col) ne "YES"
order by fmtname, start; and a.fmthash ne b.fmthash
order by type, fmtname, fmtrow;
options ibufsize=&ibufsize;
%mp_abort( %mp_abort(
iftrue=(&syscc ne 0) iftrue=(&syscc ne 0)
@@ -10149,19 +10323,21 @@ options ibufsize=&ibufsize;
) )
%if &loadtarget=YES %then %do; %if &loadtarget=YES %then %do;
/* new records plus base records that are not deleted or modified */
data &ds1; data &ds1;
merge &base_fmts(in=base) merge &base_fmts(in=base)
&outds_mod(in=mod) &outds_mod(in=mod)
&outds_add(in=add) &outds_add(in=add)
&outds_del(in=del); &outds_del(in=del);
if not del and not mod; if not del and not mod;
by fmtname start; by type fmtname fmtrow;
run; run;
/* add back the modified records */
data &stagedata; data &stagedata;
set &ds1 &outds_mod; set &ds1 &outds_mod;
run; run;
proc sort; proc sort;
by fmtname start; by type fmtname fmtrow;
run; run;
%end; %end;
/* mp abort needs to run outside of conditional blocks */ /* mp abort needs to run outside of conditional blocks */
@@ -10209,7 +10385,7 @@ options ibufsize=&ibufsize;
%mp_storediffs(&libcat-FC %mp_storediffs(&libcat-FC
,&base_fmts ,&base_fmts
,FMTNAME START ,TYPE FMTNAME FMTROW
,delds=&outds_del ,delds=&outds_del
,modds=&outds_mod ,modds=&outds_mod
,appds=&outds_add ,appds=&outds_add
@@ -10413,7 +10589,7 @@ run;
data _null_; data _null_;
putlog 'NOTE-' / 'NOTE-'; putlog 'NOTE-' / 'NOTE-';
putlog "NOTE- &sysmacroname: table locked, waiting "@; putlog "NOTE- &sysmacroname: table locked, waiting "@;
putlog "%sysfunc(sleep(&loop_inc)) seconds.. "; putlog "%sysfunc(sleep(&loop_secs)) seconds.. ";
putlog "NOTE- (iteration &x of &loops)"; putlog "NOTE- (iteration &x of &loops)";
putlog 'NOTE-' / 'NOTE-'; putlog 'NOTE-' / 'NOTE-';
run; run;
@@ -10446,7 +10622,10 @@ run;
where LOCK_LIB ="&lib" and LOCK_DS="&ds"; where LOCK_LIB ="&lib" and LOCK_DS="&ds";
quit; quit;
%if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc; %if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
%if &status=LOCKED %then %do; %if &sqlobs=0 %then %do;
%put %str(WAR)NING: &lib..&ds has never been locked!;
%end;
%else %if &status=LOCKED %then %do;
data _null_; data _null_;
putlog "&sysmacroname: unlocking &lib..&ds:"; putlog "&sysmacroname: unlocking &lib..&ds:";
run; run;
@@ -12630,9 +12809,9 @@ run;
%if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do; %if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do;
/* this is a format catalog - cannot query cols directly */ /* this is a format catalog - cannot query cols directly */
%let vlist="FMTNAME","START","END","LABEL","MIN","MAX","DEFAULT","LENGTH" %let vlist="TYPE","FMTNAME","FMTROW","START","END","LABEL","MIN","MAX"
,"FUZZ","PREFIX","MULT","FILL","NOEDIT","TYPE","SEXCL","EEXCL","HLO" ,"DEFAULT","LENGTH","FUZZ","PREFIX","MULT","FILL","NOEDIT","SEXCL"
,"DECSEP","DIG3SEP","DATATYPE","LANGUAGE"; ,"EEXCL","HLO","DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%end; %end;
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE); %else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);
@@ -13900,24 +14079,27 @@ ods package close;
%mend mddl_dc_maxkeytable;/** %mend mddl_dc_maxkeytable;/**
@file @file
@brief The CNTLOUT table generated by proc format @brief The CNTLOUT table generated by proc format
@details This table will actually change format depending on the data values, @details The actual CNTLOUT table may have varying variable lengths,
therefore the max possible lengths are described here to enable consistency depending on the data values, therefore the max possible lengths
when dealing with format data. (given various practical restrictions) are described here to enable
consistency when dealing with format data.
**/ **/
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT); %macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
proc sql; proc sql;
create table &libds( create table &libds(
FMTNAME char(32) label='Format name' TYPE char(1) label='Type of format - either N (num fmt), C (char fmt), I (num infmt) or J (char infmt)'
,FMTNAME char(32) label='Format name'
,FMTROW num label='CALCULATED Position of record by FMTNAME (reqd for multilabel formats)'
,START char(32767) label='Starting value for format'
/* /*
to accommodate larger START values, mp_loadformat.sas will need the Keep lengths of START and END the same to avoid this err:
SQL dependency removed (proc sql needs to accommodate 3 index values in "Start is greater than end: -<."
a 32767 ibufsize limit) Similar usage note: https://support.sas.com/kb/69/330.html
*/ */
,START char(10000) label='Starting value for format'
,END char(32767) label='Ending value for format' ,END char(32767) label='Ending value for format'
,LABEL char(32767) label='Format value label' ,LABEL char(32767) label='Format value label'
,MIN num length=3 label='Minimum length' ,MIN num length=3 label='Minimum length'
@@ -13929,15 +14111,26 @@ create table &libds(
,MULT num label='Multiplier' ,MULT num label='Multiplier'
,FILL char(1) label='Fill character' ,FILL char(1) label='Fill character'
,NOEDIT num length=3 label='Is picture string noedit?' ,NOEDIT num length=3 label='Is picture string noedit?'
,TYPE char(1) label='Type of format'
,SEXCL char(1) label='Start exclusion' ,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion' ,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information' ,HLO char(13) label='Additional information. M=MultiLabel'
,DECSEP char(1) label='Decimal separator' ,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator' ,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?' ,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings' ,LANGUAGE char(8) label='Language for date strings'
); );
%local lib;
%let libds=%upcase(&libds);
%if %index(&libds,.)=0 %then %let lib=WORK;
%else %let lib=%scan(&libds,1,.);
proc datasets lib=&lib noprint;
modify %scan(&libds,-1,.);
index create
pk_cntlout=(type fmtname fmtrow)
/nomiss unique;
quit;
%mend mddl_sas_cntlout; %mend mddl_sas_cntlout;
/** /**
@@ -14023,7 +14216,8 @@ run;
filename __us2grp temp; filename __us2grp temp;
proc metadata in= "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> proc metadata in= "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
<Person Id='&uuri'><IdentityGroups><IdentityGroup ObjRef='&guri' /> <Person Id='%nrstr(&uuri)'>
<IdentityGroups><IdentityGroup ObjRef='%nrstr(&guri)' />
</IdentityGroups></Person></Metadata> </IdentityGroups></Person></Metadata>
<NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>" <NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>"
out=__us2grp verbose; out=__us2grp verbose;
@@ -14040,7 +14234,8 @@ run;
filename __us2grp clear; filename __us2grp clear;
%mend mm_adduser2group;/** %mend mm_adduser2group;
/**
@file @file
@brief Assigns library directly using details from metadata @brief Assigns library directly using details from metadata
@details Queries metadata to get the libname definition then allocates the @details Queries metadata to get the libname definition then allocates the
@@ -16403,7 +16598,7 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 '; put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs '; put ' ,maxobs=&workobs ';
put ' ) '; put ' ) ';
put ' data _null_; file _sjsref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
@@ -16446,6 +16641,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) '; put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syserrortext=cats(''"'',syserrortext,''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
@@ -16476,6 +16672,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) '; put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syswarningtext=cats(''"'',syswarningtext,''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; '; put ' length memsize $32; ';
@@ -16501,9 +16698,11 @@ data _null_;
put ' '; put ' ';
put '%mend mm_webout; '; put '%mend mm_webout; ';
/* WEBOUT END */ /* WEBOUT END */
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);'; put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO';
put ' ,maxobs=MAX';
put ');';
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing'; put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta'; put ' ,showmeta=&showmeta,maxobs=&maxobs';
put ' )'; put ' )';
put '%mend;'; put '%mend;';
run; run;
@@ -20083,7 +20282,7 @@ run;
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs ,maxobs=&workobs
) )
data _null_; file _sjsref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
@@ -20126,6 +20325,7 @@ run;
prxchange('s/\\/\\\\/',-1,syserrortext) prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
@@ -20156,6 +20356,7 @@ run;
prxchange('s/\\/\\\\/',-1,syswarningtext) prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syswarningtext=cats('"',syswarningtext,'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length memsize $32; length memsize $32;
@@ -21485,7 +21686,7 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 '; put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs '; put ' ,maxobs=&workobs ';
put ' ) '; put ' ) ';
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
@@ -21524,6 +21725,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) '; put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syserrortext=cats(''"'',syserrortext,''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); '; put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); ';
put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; '; put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; ';
@@ -21557,6 +21759,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) '; put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syswarningtext=cats(''"'',syswarningtext,''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; '; put ' length memsize $32; ';
@@ -21750,7 +21953,7 @@ filename &headref clear;
@param [in] uid= (0) Provide the userid on which to filter @param [in] uid= (0) Provide the userid on which to filter
@param [out] outds= (work.ms_getgroups) This output dataset will contain the @param [out] outds= (work.ms_getgroups) This output dataset will contain the
list of groups. Format: list of groups. Format:
|NAME:$32.|DESCRIPTION:$64.|GROUPID:best.| |NAME:$32.|DESCRIPTION:$256.|GROUPID:best.|
|---|---|---| |---|---|---|
|`SomeGroup `|`A group `|`1`| |`SomeGroup `|`A group `|`1`|
|`Another Group`|`this is a different group`|`2`| |`Another Group`|`this is a different group`|`2`|
@@ -21786,7 +21989,7 @@ filename &headref clear;
%if %sysget(MODE)=desktop %then %do; %if %sysget(MODE)=desktop %then %do;
/* groups api does not exist in desktop mode */ /* groups api does not exist in desktop mode */
data &outds; data &outds;
length NAME $32 DESCRIPTION $64. GROUPID 8; length NAME $32 DESCRIPTION $256. GROUPID 8;
name="&sysuserid"; name="&sysuserid";
description="&sysuserid (group - desktop mode)"; description="&sysuserid (group - desktop mode)";
groupid=1; groupid=1;
@@ -21842,7 +22045,7 @@ libname &libref JSON fileref=&fref1;
%if "&user"="0" and "&uid"="0" %then %do; %if "&user"="0" and "&uid"="0" %then %do;
data &outds; data &outds;
length NAME $32 DESCRIPTION $64. GROUPID 8; length NAME $32 DESCRIPTION $256. GROUPID 8;
if _n_=1 then call missing(of _all_); if _n_=1 then call missing(of _all_);
set &libref..root; set &libref..root;
drop ordinal_root; drop ordinal_root;
@@ -21850,7 +22053,7 @@ libname &libref JSON fileref=&fref1;
%end; %end;
%else %do; %else %do;
data &outds; data &outds;
length NAME $32 DESCRIPTION $64. GROUPID 8; length NAME $32 DESCRIPTION $256. GROUPID 8;
if _n_=1 then call missing(of _all_); if _n_=1 then call missing(of _all_);
set &libref..groups; set &libref..groups;
drop ordinal_:; drop ordinal_:;
@@ -22062,8 +22265,9 @@ options &optval;
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquefileref.sas @li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas @li mf_getuniquename.sas
@li mp_abort.sas @li mp_abort.sas
@li mp_chop.sas
**/ **/
@@ -22176,7 +22380,10 @@ run;
run; run;
%end; %end;
filename &outref temp lrecl=32767; %local resp_path;
%let resp_path=%sysfunc(pathname(work))/%mf_getuniquename();
filename &outref "&resp_path" lrecl=32767;
/* prepare request*/ /* prepare request*/
proc http method='POST' headerin=&authref in=&mainref out=&outref proc http method='POST' headerin=&authref in=&mainref out=&outref
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131"; url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
@@ -22184,6 +22391,7 @@ proc http method='POST' headerin=&authref in=&mainref out=&outref
debug level=2; debug level=2;
%end; %end;
run; run;
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201) %if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
or &mdebug=1 or &mdebug=1
%then %do; %then %do;
@@ -22199,11 +22407,22 @@ or &mdebug=1
options &optval; options &optval;
%if &outlogds ne _null_ or &mdebug=1 %then %do; %if &outlogds ne _null_ or &mdebug=1 %then %do;
%local dumplib; %local matchstr chopout;
%let dumplib=%mf_getuniquelibref(); %let matchstr=SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784;
libname &dumplib json fileref=&outref; %let chopout=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop);
%mp_chop("&resp_path"
,matchvar=matchstr
,keep=LAST
,matchpoint=END
,outfile="&chopout"
,mdebug=&mdebug
)
data &outlogds; data &outlogds;
set &dumplib..log; infile "&chopout" lrecl=2000;
length line $2000;
line=_infile_;
%if &mdebug=1 %then %do; %if &mdebug=1 %then %do;
putlog line=; putlog line=;
%end; %end;
@@ -22330,50 +22549,38 @@ run;
) )
/* SASjs services have the _webout embedded in wrapper JSON */ /* chop out JSON section */
/* Files can also be very large - so use a dedicated macro to chop it out */ %local matchstr chopout;
%local matchstr1 matchstr2 ; %let matchstr=SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784;
%let matchstr1={"status":"success","_webout":{; %let chopout=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop);
%let matchstr2=},"log":[{;
%let chopout1=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop1);
%let chopout2=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop2);
%mp_chop("%sysfunc(pathname(&fref1,F))" %mp_chop("%sysfunc(pathname(&fref1,F))"
,matchvar=matchstr1 ,matchvar=matchstr
,keep=LAST
,matchpoint=END
,offset=-1
,outfile="&chopout1"
,mdebug=&mdebug
)
%mp_chop("&chopout1"
,matchvar=matchstr2
,keep=FIRST ,keep=FIRST
,matchpoint=START ,matchpoint=START
,offset=1 ,offset=-1
,outfile="&chopout2" ,outfile="&chopout"
,mdebug=&mdebug ,mdebug=&mdebug
) )
%if &outlib ne 0 %then %do; %if &outlib ne 0 %then %do;
libname &outlib json "&chopout2"; libname &outlib json "&chopout";
%end; %end;
%if &outref ne 0 %then %do; %if &outref ne 0 %then %do;
filename &outref "&chopout2"; filename &outref "&chopout";
%end; %end;
%if &mdebug=0 %then %do; %if &mdebug=0 %then %do;
filename &webref clear; filename &webref clear;
filename &fref1 clear; filename &fref1 clear;
filename &fref2 clear;
%end; %end;
%else %do; %else %do;
%put &sysmacroname exit vars:; %put &sysmacroname exit vars:;
%put _local_; %put _local_;
%end; %end;
%mend ms_testservice;/** %mend ms_testservice;
/**
@file @file
@brief Send data to/from sasjs/server @brief Send data to/from sasjs/server
@details This macro should be added to the start of each web service, @details This macro should be added to the start of each web service,
@@ -22516,7 +22723,7 @@ run;
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs ,maxobs=&workobs
) )
data _null_; file &fref mod encoding='utf-8' termstr=lf; data _null_; file &fref mod encoding='utf-8' termstr=lf;
@@ -22555,6 +22762,7 @@ run;
prxchange('s/\\/\\\\/',-1,syserrortext) prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG'))); SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG')));
put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG; put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG;
@@ -22588,6 +22796,7 @@ run;
prxchange('s/\\/\\\\/',-1,syswarningtext) prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syswarningtext=cats('"',syswarningtext,'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length memsize $32; length memsize $32;
@@ -24183,6 +24392,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) '; put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syserrortext=cats(''"'',syserrortext,''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
@@ -24213,6 +24423,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) '; put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syswarningtext=cats(''"'',syswarningtext,''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; '; put ' length memsize $32; ';
@@ -28024,6 +28235,7 @@ filename &fref1 clear;
prxchange('s/\\/\\\\/',-1,syserrortext) prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
@@ -28054,6 +28266,7 @@ filename &fref1 clear;
prxchange('s/\\/\\\\/',-1,syswarningtext) prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syswarningtext=cats('"',syswarningtext,'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length memsize $32; length memsize $32;

View File

@@ -12,7 +12,7 @@
**/ **/
%macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1) %macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1)
)/*/STORE SOURCE*/; )/des='ungraceful abort' /*STORE SOURCE*/;
%if not(%eval(%unquote(&iftrue))) %then %return; %if not(%eval(%unquote(&iftrue))) %then %return;
@@ -24,4 +24,4 @@
%mend mf_abort; %mend mf_abort;
/** @endcond */ /** @endcond */

View File

@@ -30,4 +30,4 @@
0 0
%end; %end;
%mend mf_existfileref; %mend mf_existfileref;

View File

@@ -25,13 +25,17 @@
%local dsid rc; %local dsid rc;
%let dsid=%sysfunc(open(&libds,is)); %let dsid=%sysfunc(open(&libds,is));
%if &dsid=0 or %length(&var)=0 %then %do; %if &dsid=0 %then %do;
%put %sysfunc(sysmsg()); %put %sysfunc(sysmsg());
0 0
%end;
%else %if %length(&var)=0 %then %do;
0
%let rc=%sysfunc(close(&dsid));
%end; %end;
%else %do; %else %do;
%sysfunc(varnum(&dsid,&var)) %sysfunc(varnum(&dsid,&var))
%let rc=%sysfunc(close(&dsid)); %let rc=%sysfunc(close(&dsid));
%end; %end;
%mend mf_existvar; %mend mf_existvar;

View File

@@ -225,15 +225,51 @@ and %superq(SYSPROCESSNAME) ne %str(Compute Server)
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ',"_PROGRAM" : ' _PROGRAM ; put ',"_PROGRAM" : ' _PROGRAM ;
put ",""SYSCC"" : ""&syscc"" "; put ",""SYSCC"" : ""&syscc"" ";
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"'); syserrortext=cats(symget('syserrortext'));
put ",""SYSERRORTEXT"" : " syserrortext; if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
syserrortext='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */
prxchange('s/\x0D/\r/',-1, /* carriage return */
prxchange('s/\x09/\\t/',-1, /* tab */
prxchange('s/\x00/\\u0000/',-1, /* NUL */
prxchange('s/\x0E/\\u000E/',-1, /* SS */
prxchange('s/\x0F/\\u000F/',-1, /* SF */
prxchange('s/\x01/\\u0001/',-1, /* SOH */
prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"';
end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSSCPL"" : ""&sysscpl"" "; put ",""SYSSCPL"" : ""&sysscpl"" ";
put ",""SYSSITE"" : ""&syssite"" "; put ",""SYSSITE"" : ""&syssite"" ";
sysvlong=quote(trim(symget('sysvlong'))); sysvlong=quote(trim(symget('sysvlong')));
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"'); syswarningtext=cats(symget('syswarningtext'));
if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
syswarningtext='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */
prxchange('s/\x0D/\r/',-1, /* carriage return */
prxchange('s/\x09/\\t/',-1, /* tab */
prxchange('s/\x00/\\u0000/',-1, /* NUL */
prxchange('s/\x0E/\\u000E/',-1, /* SS */
prxchange('s/\x0F/\\u000F/',-1, /* SF */
prxchange('s/\x01/\\u0001/',-1, /* SOH */
prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"';
end;
else syswarningtext=cats('"',syswarningtext,'"');
put ",""SYSWARNINGTEXT"" : " syswarningtext; put ",""SYSWARNINGTEXT"" : " syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
put "}" ; put "}" ;

95
base/mp_aligndecimal.sas Normal file
View File

@@ -0,0 +1,95 @@
/**
@file
@brief Apply leading blanks to align numbers vertically in a char variable
@details This is particularly useful when storing numbers (as character) that
need to be sorted.
It works by splitting the number left and right of the decimal place, and
aligning it accordingly. A temporary variable is created as part of this
process (which is automatically dropped)
The macro can be used only in data step, eg as follows:
data _null_;
length myvar $50;
do i=1 to 1000 by 50;
if mod(i,2)=0 then j=ranuni(0)*i*100;
else j=i*100;
%mp_aligndecimal(myvar,width=7)
leading_spaces=length(myvar)-length(cats(myvar));
putlog +leading_spaces myvar;
end;
run;
The generated code will look something like this:
length aligndp4e49996 $7;
if index(myvar,'.') then do;
aligndp4e49996=cats(scan(myvar,1,'.'));
aligndp4e49996=right(aligndp4e49996);
myvar=aligndp4e49996!!'.'!!cats(scan(myvar,2,'.'));
end;
else do;
aligndp4e49996=myvar;
aligndp4e49996=right(aligndp4e49996);
myvar=aligndp4e49996;
end;
drop aligndp4e49996;
Results (myvar variable):
0.7683559324
122.8232796
99419.50552
42938.5143414
763.3799189
15170.606073
15083.285773
85443.198707
2022999.2251
12038.658867
1350582.6734
52777.258221
11723.347628
33101.268376
6181622.8603
7390614.0669
73384.537893
1788362.1016
2774586.2219
7998580.8415
@param var The (data step, character) variable to modify
@param width= (8) The number of characters BEFORE the decimal point
<h4> SAS Macros </h4>
@li mf_getuniquename.sas
<h4> Related Programs </h4>
@li mp_aligndecimal.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_aligndecimal(var,width=8);
%local tmpvar;
%let tmpvar=%mf_getuniquename(prefix=aligndp);
length &tmpvar $&width;
if index(&var,'.') then do;
&tmpvar=cats(scan(&var,1,'.'));
&tmpvar=right(&tmpvar);
&var=&tmpvar!!'.'!!cats(scan(&var,2,'.'));
end;
else do;
&tmpvar=cats(&var);
&tmpvar=right(&tmpvar);
&var=&tmpvar;
end;
drop &tmpvar;
%mend mp_aligndecimal;

View File

@@ -22,7 +22,7 @@
results. If it does not exist, it will be created, with the following format: results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256| |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|---|---|---| |---|---|---|
|User Provided description|PASS|Column &inds contained ALL columns| |User Provided description|PASS|Dataset &inds contained ALL columns|
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe

View File

@@ -41,7 +41,7 @@
results. If it does not exist, it will be created, with the following format: results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256| |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|---|---|---| |---|---|---|
|User Provided description|PASS|Column &inds contained ALL columns| |User Provided description|PASS|Dataset &inds contained ALL columns|
<h4> Related Macros </h4> <h4> Related Macros </h4>

View File

@@ -42,6 +42,7 @@
@param [in] test= (ALLVALS) The test to apply. Valid values are: @param [in] test= (ALLVALS) The test to apply. Valid values are:
@li ALLVALS - Test is a PASS if ALL values have a match in checkvals @li ALLVALS - Test is a PASS if ALL values have a match in checkvals
@li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals @li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals
@li NOVAL - Test is a PASS if there are NO matches in checkvals
@param [out] outds= (work.test_results) The output dataset to contain the @param [out] outds= (work.test_results) The output dataset to contain the
results. If it does not exist, it will be created, with the following format: results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256| |TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
@@ -97,7 +98,7 @@
%let test=%upcase(&test); %let test=%upcase(&test);
%if &test ne ALLVALS and &test ne ANYVAL %then %do; %if &test ne ALLVALS and &test ne ANYVAL and &test ne NOVAL %then %do;
%mp_abort( %mp_abort(
mac=&sysmacroname, mac=&sysmacroname,
msg=%str(Invalid test - &test) msg=%str(Invalid test - &test)
@@ -108,12 +109,12 @@
%let result=-1; %let result=-1;
%let orig=-1; %let orig=-1;
proc sql noprint; proc sql noprint;
select count(*) into: result select count(*) into: result trimmed
from &lib..&ds from &lib..&ds
where &col not in ( where &col not in (
select &ccol from &clib..&cds select &ccol from &clib..&cds
); );
select count(*) into: orig from &lib..&ds; select count(*) into: orig trimmed from &lib..&ds;
quit; quit;
%local notfound tmp1 tmp2; %local notfound tmp1 tmp2;
@@ -145,7 +146,7 @@
length test_description $256 test_result $4 test_comments $256; length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc'); test_description=symget('desc');
test_result='FAIL'; test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result values " test_comments="&sysmacroname: &lib..&ds..&col has &result/&orig values "
!!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound'); !!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
%if &test=ANYVAL %then %do; %if &test=ANYVAL %then %do;
if &result < &orig then test_result='PASS'; if &result < &orig then test_result='PASS';
@@ -153,6 +154,9 @@
%else %if &test=ALLVALS %then %do; %else %if &test=ALLVALS %then %do;
if &result=0 then test_result='PASS'; if &result=0 then test_result='PASS';
%end; %end;
%else %if &test=NOVAL %then %do;
if &result=&orig then test_result='PASS';
%end;
%else %do; %else %do;
test_comments="&sysmacroname: Unsatisfied test condition - &test"; test_comments="&sysmacroname: Unsatisfied test condition - &test";
%end; %end;

View File

@@ -25,6 +25,7 @@
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas @li mddl_sas_cntlout.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mp_aligndecimal.sas
<h4> Related Macros </h4> <h4> Related Macros </h4>
@li mf_getvarformat.sas @li mf_getvarformat.sas
@@ -66,16 +67,24 @@ proc format lib=&libcat cntlout=&cntlds;
%end; %end;
run; run;
data &cntlout; data &cntlout/nonote2err;
if 0 then set &ddlds; if 0 then set &ddlds;
set &cntlds; set &cntlds;
if type="N" then do; by type fmtname notsorted;
start=cats(start);
end=cats(end); /* align the numeric values to avoid overlapping ranges */
if type in ("I","N") then do;
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
end; end;
/* create row marker. Data cannot be sorted without it! */
if first.fmtname then fmtrow=0;
fmtrow+1;
run; run;
proc sort; proc sort;
by fmtname start; by type fmtname fmtrow;
run; run;
proc sql; proc sql;

View File

@@ -95,6 +95,7 @@ run;
options ps=max lrecl=max; options ps=max lrecl=max;
data _null_; data _null_;
infile &outref; infile &outref;
if _n_=1 then putlog "# &libds" /;
input; input;
putlog _infile_; putlog _infile_;
run; run;

View File

@@ -34,10 +34,10 @@
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs @param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_nobs.sas @li mf_nobs.sas
@li mp_abort.sas @li mp_abort.sas
@li mp_aligndecimal.sas
@li mp_cntlout.sas @li mp_cntlout.sas
@li mp_lockanytable.sas @li mp_lockanytable.sas
@li mp_storediffs.sas @li mp_storediffs.sas
@@ -45,7 +45,8 @@
<h4> Related Macros </h4> <h4> Related Macros </h4>
@li mddl_dc_difftable.sas @li mddl_dc_difftable.sas
@li mddl_dc_locktable.sas @li mddl_dc_locktable.sas
@li mp_loadformat.test.sas @li mp_loadformat.test.1.sas
@li mp_loadformat.test.2.sas
@li mp_lockanytable.sas @li mp_lockanytable.sas
@li mp_stackdiffs.sas @li mp_stackdiffs.sas
@@ -78,13 +79,6 @@
%let &var=%upcase(&prefix._&var); %let &var=%upcase(&prefix._&var);
%end; %end;
/*
format values can be up to 32767 wide. SQL joins on such a wide column can
cause buffer issues. Update ibufsize and reset at the end.
*/
%let ibufsize=%sysfunc(getoption(ibufsize));
options ibufsize=32767 ;
/* in DC, format catalogs maybe specified in the libds with a -FC extension */ /* in DC, format catalogs maybe specified in the libds with a -FC extension */
%let libcat=%scan(&libcat,1,-); %let libcat=%scan(&libcat,1,-);
@@ -134,29 +128,62 @@ run;
* First, extract only relevant formats from the catalog * First, extract only relevant formats from the catalog
*/ */
proc sql noprint; proc sql noprint;
select distinct upcase(fmtname) into: fmtlist separated by ' ' from &libds; select distinct
case
when type='N' then upcase(fmtname)
when type='C' then cats('$',upcase(fmtname))
when type='I' then cats('@',upcase(fmtname))
when type='J' then cats('@$',upcase(fmtname))
else "&sysmacroname:UNHANDLED"
end
into: fmtlist separated by ' '
from &libds;
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts) %mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
/* get a hash of the row */
%local cvars nvars;
%let cvars=TYPE FMTNAME START END LABEL PREFIX FILL SEXCL EEXCL HLO DECSEP
DIG3SEP DATATYPE LANGUAGE;
%let nvars=FMTROW MIN MAX DEFAULT LENGTH FUZZ MULT NOEDIT;
data &base_fmts/note2err;
set &base_fmts;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/** /**
* Ensure input table and base_formats have consistent lengths and types * Ensure input table and base_formats have consistent lengths and types
*/ */
%mddl_sas_cntlout(libds=&template) data &inlibds/nonote2err;
data &inlibds; length &delete_col $3 FMTROW 8 start end label $32767;
length &delete_col $3; if 0 then set &base_fmts;
if 0 then set &template;
set &libds; set &libds;
by type fmtname notsorted;
if &delete_col='' then &delete_col='No'; if &delete_col='' then &delete_col='No';
fmtname=upcase(fmtname); fmtname=upcase(fmtname);
type=upcase(type);
if missing(type) then do; if missing(type) then do;
if substr(fmtname,1,1)='$' then type='C'; if substr(fmtname,1,1)='@' then do;
else type='N'; if substr(fmtname,2,1)='$' then type='J';
else type='I';
end;
else do;
if substr(fmtname,1,1)='$' then type='C';
else type='N';
end;
end; end;
if type='N' then do; if type in ('N','I') then do;
start=cats(start); %mp_aligndecimal(start,width=16)
end=cats(end); %mp_aligndecimal(end,width=16)
end; end;
/* update row marker - retain new var as fmtrow may already be in libds */
if first.fmtname then row=1;
else row+1;
drop row;
fmtrow=row;
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run; run;
/** /**
@@ -167,11 +194,10 @@ create table &outds_add(drop=&delete_col) as
select a.* select a.*
from &inlibds a from &inlibds a
left join &base_fmts b left join &base_fmts b
on a.fmtname=b.fmtname on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
and a.start=b.start
where b.fmtname is null where b.fmtname is null
and upcase(a.&delete_col) ne "YES" and upcase(a.&delete_col) ne "YES"
order by fmtname, start;; order by type, fmtname, fmtrow;
/** /**
* Identify deleted records * Identify deleted records
@@ -180,10 +206,9 @@ create table &outds_del(drop=&delete_col) as
select a.* select a.*
from &inlibds a from &inlibds a
inner join &base_fmts b inner join &base_fmts b
on a.fmtname=b.fmtname on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
and a.start=b.start
where upcase(a.&delete_col)="YES" where upcase(a.&delete_col)="YES"
order by fmtname, start; order by type, fmtname, fmtrow;
/** /**
* Identify modified records * Identify modified records
@@ -192,12 +217,10 @@ create table &outds_mod (drop=&delete_col) as
select a.* select a.*
from &inlibds a from &inlibds a
inner join &base_fmts b inner join &base_fmts b
on a.fmtname=b.fmtname on a.type=b.type and a.fmtname=b.fmtname and a.fmtrow=b.fmtrow
and a.start=b.start
where upcase(a.&delete_col) ne "YES" where upcase(a.&delete_col) ne "YES"
order by fmtname, start; and a.fmthash ne b.fmthash
order by type, fmtname, fmtrow;
options ibufsize=&ibufsize;
%mp_abort( %mp_abort(
iftrue=(&syscc ne 0) iftrue=(&syscc ne 0)
@@ -206,19 +229,21 @@ options ibufsize=&ibufsize;
) )
%if &loadtarget=YES %then %do; %if &loadtarget=YES %then %do;
/* new records plus base records that are not deleted or modified */
data &ds1; data &ds1;
merge &base_fmts(in=base) merge &base_fmts(in=base)
&outds_mod(in=mod) &outds_mod(in=mod)
&outds_add(in=add) &outds_add(in=add)
&outds_del(in=del); &outds_del(in=del);
if not del and not mod; if not del and not mod;
by fmtname start; by type fmtname fmtrow;
run; run;
/* add back the modified records */
data &stagedata; data &stagedata;
set &ds1 &outds_mod; set &ds1 &outds_mod;
run; run;
proc sort; proc sort;
by fmtname start; by type fmtname fmtrow;
run; run;
%end; %end;
/* mp abort needs to run outside of conditional blocks */ /* mp abort needs to run outside of conditional blocks */
@@ -266,7 +291,7 @@ options ibufsize=&ibufsize;
%mp_storediffs(&libcat-FC %mp_storediffs(&libcat-FC
,&base_fmts ,&base_fmts
,FMTNAME START ,TYPE FMTNAME FMTROW
,delds=&outds_del ,delds=&outds_del
,modds=&outds_mod ,modds=&outds_mod
,appds=&outds_add ,appds=&outds_add

View File

@@ -167,7 +167,7 @@ run;
data _null_; data _null_;
putlog 'NOTE-' / 'NOTE-'; putlog 'NOTE-' / 'NOTE-';
putlog "NOTE- &sysmacroname: table locked, waiting "@; putlog "NOTE- &sysmacroname: table locked, waiting "@;
putlog "%sysfunc(sleep(&loop_inc)) seconds.. "; putlog "%sysfunc(sleep(&loop_secs)) seconds.. ";
putlog "NOTE- (iteration &x of &loops)"; putlog "NOTE- (iteration &x of &loops)";
putlog 'NOTE-' / 'NOTE-'; putlog 'NOTE-' / 'NOTE-';
run; run;
@@ -200,7 +200,10 @@ run;
where LOCK_LIB ="&lib" and LOCK_DS="&ds"; where LOCK_LIB ="&lib" and LOCK_DS="&ds";
quit; quit;
%if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc; %if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
%if &status=LOCKED %then %do; %if &sqlobs=0 %then %do;
%put %str(WAR)NING: &lib..&ds has never been locked!;
%end;
%else %if &status=LOCKED %then %do;
data _null_; data _null_;
putlog "&sysmacroname: unlocking &lib..&ds:"; putlog "&sysmacroname: unlocking &lib..&ds:";
run; run;

View File

@@ -147,9 +147,9 @@ run;
%if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do; %if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do;
/* this is a format catalog - cannot query cols directly */ /* this is a format catalog - cannot query cols directly */
%let vlist="FMTNAME","START","END","LABEL","MIN","MAX","DEFAULT","LENGTH" %let vlist="TYPE","FMTNAME","FMTROW","START","END","LABEL","MIN","MAX"
,"FUZZ","PREFIX","MULT","FILL","NOEDIT","TYPE","SEXCL","EEXCL","HLO" ,"DEFAULT","LENGTH","FUZZ","PREFIX","MULT","FILL","NOEDIT","SEXCL"
,"DECSEP","DIG3SEP","DATATYPE","LANGUAGE"; ,"EEXCL","HLO","DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%end; %end;
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE); %else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);

View File

@@ -1,24 +1,27 @@
/** /**
@file @file
@brief The CNTLOUT table generated by proc format @brief The CNTLOUT table generated by proc format
@details This table will actually change format depending on the data values, @details The actual CNTLOUT table may have varying variable lengths,
therefore the max possible lengths are described here to enable consistency depending on the data values, therefore the max possible lengths
when dealing with format data. (given various practical restrictions) are described here to enable
consistency when dealing with format data.
**/ **/
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT); %macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
proc sql; proc sql;
create table &libds( create table &libds(
FMTNAME char(32) label='Format name' TYPE char(1) label='Type of format - either N (num fmt), C (char fmt), I (num infmt) or J (char infmt)'
,FMTNAME char(32) label='Format name'
,FMTROW num label='CALCULATED Position of record by FMTNAME (reqd for multilabel formats)'
,START char(32767) label='Starting value for format'
/* /*
to accommodate larger START values, mp_loadformat.sas will need the Keep lengths of START and END the same to avoid this err:
SQL dependency removed (proc sql needs to accommodate 3 index values in "Start is greater than end: -<."
a 32767 ibufsize limit) Similar usage note: https://support.sas.com/kb/69/330.html
*/ */
,START char(10000) label='Starting value for format'
,END char(32767) label='Ending value for format' ,END char(32767) label='Ending value for format'
,LABEL char(32767) label='Format value label' ,LABEL char(32767) label='Format value label'
,MIN num length=3 label='Minimum length' ,MIN num length=3 label='Minimum length'
@@ -30,14 +33,25 @@ create table &libds(
,MULT num label='Multiplier' ,MULT num label='Multiplier'
,FILL char(1) label='Fill character' ,FILL char(1) label='Fill character'
,NOEDIT num length=3 label='Is picture string noedit?' ,NOEDIT num length=3 label='Is picture string noedit?'
,TYPE char(1) label='Type of format'
,SEXCL char(1) label='Start exclusion' ,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion' ,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information' ,HLO char(13) label='Additional information. M=MultiLabel'
,DECSEP char(1) label='Decimal separator' ,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator' ,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?' ,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings' ,LANGUAGE char(8) label='Language for date strings'
); );
%local lib;
%let libds=%upcase(&libds);
%if %index(&libds,.)=0 %then %let lib=WORK;
%else %let lib=%scan(&libds,1,.);
proc datasets lib=&lib noprint;
modify %scan(&libds,-1,.);
index create
pk_cntlout=(type fmtname fmtrow)
/nomiss unique;
quit;
%mend mddl_sas_cntlout; %mend mddl_sas_cntlout;

View File

@@ -81,7 +81,8 @@ run;
filename __us2grp temp; filename __us2grp temp;
proc metadata in= "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> proc metadata in= "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
<Person Id='&uuri'><IdentityGroups><IdentityGroup ObjRef='&guri' /> <Person Id='%nrstr(&uuri)'>
<IdentityGroups><IdentityGroup ObjRef='%nrstr(&guri)' />
</IdentityGroups></Person></Metadata> </IdentityGroups></Person></Metadata>
<NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>" <NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>"
out=__us2grp verbose; out=__us2grp verbose;
@@ -98,4 +99,4 @@ run;
filename __us2grp clear; filename __us2grp clear;
%mend mm_adduser2group; %mend mm_adduser2group;

View File

@@ -546,7 +546,7 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 '; put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs '; put ' ,maxobs=&workobs ';
put ' ) '; put ' ) ';
put ' data _null_; file _sjsref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
@@ -589,6 +589,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) '; put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syserrortext=cats(''"'',syserrortext,''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
@@ -619,6 +620,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) '; put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syswarningtext=cats(''"'',syswarningtext,''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; '; put ' length memsize $32; ';
@@ -644,9 +646,11 @@ data _null_;
put ' '; put ' ';
put '%mend mm_webout; '; put '%mend mm_webout; ';
/* WEBOUT END */ /* WEBOUT END */
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);'; put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO';
put ' ,maxobs=MAX';
put ');';
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing'; put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta'; put ' ,showmeta=&showmeta,maxobs=&maxobs';
put ' )'; put ' )';
put '%mend;'; put '%mend;';
run; run;

View File

@@ -150,7 +150,7 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs ,maxobs=&workobs
) )
data _null_; file _sjsref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
@@ -193,6 +193,7 @@
prxchange('s/\\/\\\\/',-1,syserrortext) prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
@@ -223,6 +224,7 @@
prxchange('s/\\/\\\\/',-1,syswarningtext) prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syswarningtext=cats('"',syswarningtext,'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length memsize $32; length memsize $32;

989
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,6 @@
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true" "prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
}, },
"devDependencies": { "devDependencies": {
"@sasjs/cli": "3.13.0" "@sasjs/cli": "3.24.0"
} }
} }

View File

@@ -85,7 +85,7 @@
{ {
"name": "docsonly", "name": "docsonly",
"serverType": "SASJS", "serverType": "SASJS",
"appLoc": "dummy", "appLoc": "/dummy",
"macroFolders": [ "macroFolders": [
"meta", "meta",
"metax", "metax",

View File

@@ -1,224 +1,224 @@
/** /**
@file @file
@brief Deploy repo as a SAS PACKAGES module @brief Deploy repo as a SAS PACKAGES module
@details After every release, this program is executed to update the SASPAC @details After every release, this program is executed to update the SASPAC
repo with the latest macros (and same version number). repo with the latest macros (and same version number).
The program is first compiled using sasjs compile, then executed using The program is first compiled using sasjs compile, then executed using
sasjs run. sasjs run.
Requires the server to have SSH keys. Requires the server to have SSH keys.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_gitadd.sas @li mp_gitadd.sas
@li mp_gitreleaseinfo.sas @li mp_gitreleaseinfo.sas
@li mp_gitstatus.sas @li mp_gitstatus.sas
**/ **/
/* get package version */ /* get package version */
%mp_gitreleaseinfo(GITHUB,sasjs/core,outlib=splib) %mp_gitreleaseinfo(GITHUB,sasjs/core,outlib=splib)
data _null_; data _null_;
set splib.root; set splib.root;
call symputx('version',substr(TAG_NAME,2)); call symputx('version',substr(TAG_NAME,2));
run; run;
/* clone the source repo */ /* clone the source repo */
%let dir = %sysfunc(pathname(work))/core; %let dir = %sysfunc(pathname(work))/core;
%put source clone rc=%sysfunc(GITFN_CLONE(https://github.com/sasjs/core,&dir)); %put source clone rc=%sysfunc(GITFN_CLONE(https://github.com/sasjs/core,&dir));
/* /*
clone the target repo. clone the target repo.
If you have issues, see: https://stackoverflow.com/questions/74082874 If you have issues, see: https://stackoverflow.com/questions/74082874
*/ */
options dlcreatedir; options dlcreatedir;
%let dirOut = %sysfunc(pathname(work))/package; %let dirOut = %sysfunc(pathname(work))/package;
libname _ "&dirOut."; libname _ "&dirOut.";
%put tgt clone rc=%sysfunc(GITFN_CLONE( %put tgt clone rc=%sysfunc(GITFN_CLONE(
git@github.com:SASPAC/sasjscore.git, git@github.com:SASPAC/sasjscore.git,
&dirOut, &dirOut,
git, git,
%str( ), %str( ),
/home/sasjssrv/.ssh/id_ecdsa.pub, /home/sasjssrv/.ssh/id_ecdsa.pub,
/home/sasjssrv/.ssh/id_ecdsa /home/sasjssrv/.ssh/id_ecdsa
)); ));
/* /*
Prepare Package Metadata Prepare Package Metadata
*/ */
data _null_; data _null_;
infile CARDS4; infile CARDS4;
file "&dirOut./description.sas"; file "&dirOut./description.sas";
input; input;
if _infile_ =: 'Version:' then put "Version: &version."; if _infile_ =: 'Version:' then put "Version: &version.";
else put _infile_; else put _infile_;
CARDS4; CARDS4;
Type: Package Type: Package
Package: SASjsCore Package: SASjsCore
Title: SAS Macros for Application Development Title: SAS Macros for Application Development
Version: $(PLACEHOLDER) Version: $(PLACEHOLDER)
Author: Allan Bowe Author: Allan Bowe
Maintainer: 4GL Ltd Maintainer: 4GL Ltd
License: MIT License: MIT
Encoding: UTF8 Encoding: UTF8
DESCRIPTION START: DESCRIPTION START:
The SASjs Macro Core library is a component of the SASjs framework, the The SASjs Macro Core library is a component of the SASjs framework, the
source for which is avaible here: https://github.com/sasjs source for which is avaible here: https://github.com/sasjs
Macros are divided by: Macros are divided by:
* Macro Functions (prefix mf_) * Macro Functions (prefix mf_)
* Macro Procedures (prefix mp_) * Macro Procedures (prefix mp_)
* Macros for Metadata (prefix mm_) * Macros for Metadata (prefix mm_)
* Macros for SASjs Server (prefix ms_) * Macros for SASjs Server (prefix ms_)
* Macros for Viya (prefix mv_) * Macros for Viya (prefix mv_)
DESCRIPTION END: DESCRIPTION END:
;;;; ;;;;
run; run;
/* /*
Prepare Package License Prepare Package License
*/ */
data _null_; data _null_;
file "&dirOut./license.sas"; file "&dirOut./license.sas";
infile "&dir/LICENSE"; infile "&dir/LICENSE";
input; input;
put _infile_; put _infile_;
run; run;
/* /*
Extract Core files into MacroCore Package location Extract Core files into MacroCore Package location
*/ */
data members(compress=char); data members(compress=char);
length dref dref2 $ 8 name name2 $ 32 path $ 2048; length dref dref2 $ 8 name name2 $ 32 path $ 2048;
rc = filename(dref, "&dir."); rc = filename(dref, "&dir.");
put dref=; put dref=;
did = dopen(dref); did = dopen(dref);
if did then if did then
do i = 1 to dnum(did); do i = 1 to dnum(did);
name = dread(did, i); name = dread(did, i);
if name in if name in
("base" "ddl" "fcmp" "lua" "meta" "metax" "server" "viya" "xplatform") ("base" "ddl" "fcmp" "lua" "meta" "metax" "server" "viya" "xplatform")
then do; then do;
rc = filename(dref2,catx("/", "&dir.", name)); rc = filename(dref2,catx("/", "&dir.", name));
put dref2= name; put dref2= name;
did2 = dopen(dref2); did2 = dopen(dref2);
if did2 then if did2 then
do j = 1 to dnum(did2); do j = 1 to dnum(did2);
name2 = dread(did2, j); name2 = dread(did2, j);
path = catx("/", "&dir.", name, name2); path = catx("/", "&dir.", name, name2);
if "sas" = scan(name2, -1, ".") then output; if "sas" = scan(name2, -1, ".") then output;
end; end;
rc = dclose(did2); rc = dclose(did2);
rc = filename(dref2); rc = filename(dref2);
end; end;
end; end;
rc = dclose(did); rc = dclose(did);
rc = filename(dref); rc = filename(dref);
keep name name2 path; keep name name2 path;
run; run;
%let temp_options = %sysfunc(getoption(source)) %sysfunc(getoption(notes)); %let temp_options = %sysfunc(getoption(source)) %sysfunc(getoption(notes));
options nosource nonotes; options nosource nonotes;
data _null_; data _null_;
set members; set members;
by name notsorted; by name notsorted;
ord + first.name; ord + first.name;
if first.name then if first.name then
do; do;
call execute('libname _ ' call execute('libname _ '
!! quote(catx("/", "&dirOut.", put(ord, z3.)!!"_macros")) !! quote(catx("/", "&dirOut.", put(ord, z3.)!!"_macros"))
!! ";" !! ";"
); );
put @1 "./" ord z3. "_macros/"; put @1 "./" ord z3. "_macros/";
end; end;
put @10 name2; put @10 name2;
call execute(" call execute("
data _null_; data _null_;
infile " !! quote(strip(path)) !! "; infile " !! quote(strip(path)) !! ";
file " !! quote(catx("/", "&dirOut.", put(ord, z3.)!!"_macros", name2)) !!"; file " !! quote(catx("/", "&dirOut.", put(ord, z3.)!!"_macros", name2)) !!";
input; input;
select; select;
when (2 = trigger) put _infile_; when (2 = trigger) put _infile_;
when (_infile_ = '/**') do; put '/*** HELP START ***//**'; trigger+1; end; when (_infile_ = '/**') do; put '/*** HELP START ***//**'; trigger+1; end;
when (_infile_ = '**/') do; put '**//*** HELP END ***/'; trigger+1; end; when (_infile_ = '**/') do; put '**//*** HELP END ***/'; trigger+1; end;
otherwise put _infile_; otherwise put _infile_;
end; end;
run;"); run;");
run; run;
options &temp_options.; options &temp_options.;
/* /*
Generate SASjsCore Package Generate SASjsCore Package
*/ */
%GeneratePackage( %GeneratePackage(
filesLocation=&dirOut filesLocation=&dirOut
) )
/** /**
* apply new version in a github action * apply new version in a github action
* 1. create folder * 1. create folder
* 2. create template yaml * 2. create template yaml
* 3. replace version number * 3. replace version number
*/ */
%mf_mkdir(&dirout/.github/workflows) %mf_mkdir(&dirout/.github/workflows)
%let desc=Version &version of sasjs/core is now on SAS PACKAGES :ok_hand:; %let desc=Version &version of sasjs/core is now on SAS PACKAGES :ok_hand:;
data _null_; data _null_;
file "&dirout/.github/workflows/release.yml"; file "&dirout/.github/workflows/release.yml";
put "name: SASjs Core Package Publish Tag"; put "name: SASjs Core Package Publish Tag";
put "on:"; put "on:";
put " push:"; put " push:";
put " branches:"; put " branches:";
put " - main"; put " - main";
put "jobs:"; put "jobs:";
put " update:"; put " update:";
put " runs-on: ubuntu-latest"; put " runs-on: ubuntu-latest";
put " steps:"; put " steps:";
put " - uses: actions/checkout@master"; put " - uses: actions/checkout@master";
put " - name: Make Release"; put " - name: Make Release";
put " uses: alice-biometrics/release-creator/@v1.0.5"; put " uses: alice-biometrics/release-creator/@v1.0.5";
put " with:"; put " with:";
put " github_token: ${{ secrets.GH_TOKEN }}"; put " github_token: ${{ secrets.GH_TOKEN }}";
put " branch: main"; put " branch: main";
put " draft: false"; put " draft: false";
put " version: &version"; put " version: &version";
put " description: '&desc'"; put " description: '&desc'";
run; run;
/** /**
* Add, Commit & Push! * Add, Commit & Push!
*/ */
%mp_gitstatus(&dirout,outds=work.gitstatus,mdebug=1) %mp_gitstatus(&dirout,outds=work.gitstatus,mdebug=1)
%mp_gitadd(&dirout,inds=work.gitstatus,mdebug=1) %mp_gitadd(&dirout,inds=work.gitstatus,mdebug=1)
data _null_; data _null_;
rc=gitfn_commit("&dirout" rc=gitfn_commit("&dirout"
,"HEAD","&sysuserid","sasjs@core" ,"HEAD","&sysuserid","sasjs@core"
,"FEAT: Releasing &version" ,"FEAT: Releasing &version"
); );
put rc=; put rc=;
rc=git_push( rc=git_push(
"&dirout" "&dirout"
,"git" ,"git"
,"" ,""
,"/home/sasjssrv/.ssh/id_ecdsa.pub" ,"/home/sasjssrv/.ssh/id_ecdsa.pub"
,"/home/sasjssrv/.ssh/id_ecdsa" ,"/home/sasjssrv/.ssh/id_ecdsa"
); );
run; run;

View File

@@ -539,7 +539,7 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 '; put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs '; put ' ,maxobs=&workobs ';
put ' ) '; put ' ) ';
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
@@ -578,6 +578,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) '; put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syserrortext=cats(''"'',syserrortext,''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); '; put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); ';
put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; '; put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; ';
@@ -611,6 +612,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) '; put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syswarningtext=cats(''"'',syswarningtext,''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; '; put ' length memsize $32; ';

View File

@@ -22,7 +22,7 @@
@param [in] uid= (0) Provide the userid on which to filter @param [in] uid= (0) Provide the userid on which to filter
@param [out] outds= (work.ms_getgroups) This output dataset will contain the @param [out] outds= (work.ms_getgroups) This output dataset will contain the
list of groups. Format: list of groups. Format:
|NAME:$32.|DESCRIPTION:$64.|GROUPID:best.| |NAME:$32.|DESCRIPTION:$256.|GROUPID:best.|
|---|---|---| |---|---|---|
|`SomeGroup `|`A group `|`1`| |`SomeGroup `|`A group `|`1`|
|`Another Group`|`this is a different group`|`2`| |`Another Group`|`this is a different group`|`2`|
@@ -58,7 +58,7 @@
%if %sysget(MODE)=desktop %then %do; %if %sysget(MODE)=desktop %then %do;
/* groups api does not exist in desktop mode */ /* groups api does not exist in desktop mode */
data &outds; data &outds;
length NAME $32 DESCRIPTION $64. GROUPID 8; length NAME $32 DESCRIPTION $256. GROUPID 8;
name="&sysuserid"; name="&sysuserid";
description="&sysuserid (group - desktop mode)"; description="&sysuserid (group - desktop mode)";
groupid=1; groupid=1;
@@ -114,7 +114,7 @@ libname &libref JSON fileref=&fref1;
%if "&user"="0" and "&uid"="0" %then %do; %if "&user"="0" and "&uid"="0" %then %do;
data &outds; data &outds;
length NAME $32 DESCRIPTION $64. GROUPID 8; length NAME $32 DESCRIPTION $256. GROUPID 8;
if _n_=1 then call missing(of _all_); if _n_=1 then call missing(of _all_);
set &libref..root; set &libref..root;
drop ordinal_root; drop ordinal_root;
@@ -122,7 +122,7 @@ libname &libref JSON fileref=&fref1;
%end; %end;
%else %do; %else %do;
data &outds; data &outds;
length NAME $32 DESCRIPTION $64. GROUPID 8; length NAME $32 DESCRIPTION $256. GROUPID 8;
if _n_=1 then call missing(of _all_); if _n_=1 then call missing(of _all_);
set &libref..groups; set &libref..groups;
drop ordinal_:; drop ordinal_:;

View File

@@ -39,8 +39,9 @@
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquefileref.sas @li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas @li mf_getuniquename.sas
@li mp_abort.sas @li mp_abort.sas
@li mp_chop.sas
**/ **/
@@ -153,7 +154,10 @@ run;
run; run;
%end; %end;
filename &outref temp lrecl=32767; %local resp_path;
%let resp_path=%sysfunc(pathname(work))/%mf_getuniquename();
filename &outref "&resp_path" lrecl=32767;
/* prepare request*/ /* prepare request*/
proc http method='POST' headerin=&authref in=&mainref out=&outref proc http method='POST' headerin=&authref in=&mainref out=&outref
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131"; url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
@@ -161,6 +165,7 @@ proc http method='POST' headerin=&authref in=&mainref out=&outref
debug level=2; debug level=2;
%end; %end;
run; run;
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201) %if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
or &mdebug=1 or &mdebug=1
%then %do; %then %do;
@@ -176,11 +181,22 @@ or &mdebug=1
options &optval; options &optval;
%if &outlogds ne _null_ or &mdebug=1 %then %do; %if &outlogds ne _null_ or &mdebug=1 %then %do;
%local dumplib; %local matchstr chopout;
%let dumplib=%mf_getuniquelibref(); %let matchstr=SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784;
libname &dumplib json fileref=&outref; %let chopout=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop);
%mp_chop("&resp_path"
,matchvar=matchstr
,keep=LAST
,matchpoint=END
,outfile="&chopout"
,mdebug=&mdebug
)
data &outlogds; data &outlogds;
set &dumplib..log; infile "&chopout" lrecl=2000;
length line $2000;
line=_infile_;
%if &mdebug=1 %then %do; %if &mdebug=1 %then %do;
putlog line=; putlog line=;
%end; %end;

View File

@@ -108,47 +108,34 @@ run;
) )
/* SASjs services have the _webout embedded in wrapper JSON */ /* chop out JSON section */
/* Files can also be very large - so use a dedicated macro to chop it out */ %local matchstr chopout;
%local matchstr1 matchstr2 ; %let matchstr=SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784;
%let matchstr1={"status":"success","_webout":{; %let chopout=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop);
%let matchstr2=},"log":[{;
%let chopout1=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop1);
%let chopout2=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop2);
%mp_chop("%sysfunc(pathname(&fref1,F))" %mp_chop("%sysfunc(pathname(&fref1,F))"
,matchvar=matchstr1 ,matchvar=matchstr
,keep=LAST
,matchpoint=END
,offset=-1
,outfile="&chopout1"
,mdebug=&mdebug
)
%mp_chop("&chopout1"
,matchvar=matchstr2
,keep=FIRST ,keep=FIRST
,matchpoint=START ,matchpoint=START
,offset=1 ,offset=-1
,outfile="&chopout2" ,outfile="&chopout"
,mdebug=&mdebug ,mdebug=&mdebug
) )
%if &outlib ne 0 %then %do; %if &outlib ne 0 %then %do;
libname &outlib json "&chopout2"; libname &outlib json "&chopout";
%end; %end;
%if &outref ne 0 %then %do; %if &outref ne 0 %then %do;
filename &outref "&chopout2"; filename &outref "&chopout";
%end; %end;
%if &mdebug=0 %then %do; %if &mdebug=0 %then %do;
filename &webref clear; filename &webref clear;
filename &fref1 clear; filename &fref1 clear;
filename &fref2 clear;
%end; %end;
%else %do; %else %do;
%put &sysmacroname exit vars:; %put &sysmacroname exit vars:;
%put _local_; %put _local_;
%end; %end;
%mend ms_testservice; %mend ms_testservice;

View File

@@ -141,7 +141,7 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs ,maxobs=&workobs
) )
data _null_; file &fref mod encoding='utf-8' termstr=lf; data _null_; file &fref mod encoding='utf-8' termstr=lf;
@@ -180,6 +180,7 @@
prxchange('s/\\/\\\\/',-1,syserrortext) prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG'))); SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG')));
put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG; put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG;
@@ -213,6 +214,7 @@
prxchange('s/\\/\\\\/',-1,syswarningtext) prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syswarningtext=cats('"',syswarningtext,'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length memsize $32; length memsize $32;

View File

@@ -17,4 +17,24 @@
%mp_assert( %mp_assert(
iftrue=(%mf_existvar(sashelp.class,isjustanumber)=0), iftrue=(%mf_existvar(sashelp.class,isjustanumber)=0),
desc=Checking non existing var does not exist desc=Checking non existing var does not exist
)
data work.lockcheck;
a=1;
output;
stop;
run;
%mp_assert(
iftrue=(%mf_existvar(work.lockcheck,)=0),
desc=Checking non-provided var does not exist
)
proc sql;
update work.lockcheck set a=2;
%mp_assert(
iftrue=(&syscc=0),
desc=Checking the lock was released,
outds=work.test_results
) )

View File

@@ -0,0 +1,46 @@
/**
@file
@brief Testing mp_aligndecimal macro
@details Creates an aligned variable and checks the number of leading blanks
<h4> SAS Macros </h4>
@li mp_aligndecimal.sas
@li mp_assertcolvals.sas
@li mp_assertscope.sas
**/
/* target values */
data work.checkds;
do checkval=' 0.56',' 123.45',' 123.4 ',' 1.2 ',' 0';
output;
end;
run;
/* raw values */
data work.rawds;
set work.checkds;
tgtvar=cats(checkval);
drop checkval;
run;
%mp_assertcolvals(work.rawds.tgtvar,
checkvals=work.checkds.checkval,
desc=No values match (ready to align),
test=NOVAL
)
/* aligned values */
%mp_assertscope(SNAPSHOT)
data work.finalds;
set work.rawds;
%mp_aligndecimal(tgtvar,width=4)
run;
%mp_assertscope(COMPARE)
%mp_assertcolvals(work.finalds.tgtvar,
checkvals=work.checkds.checkval,
desc=All values match (aligned),
test=ALLVALS
)

View File

@@ -7,23 +7,35 @@
@li mp_assertcols.sas @li mp_assertcols.sas
@li mp_assertcolvals.sas @li mp_assertcolvals.sas
@li mp_assertdsobs.sas @li mp_assertdsobs.sas
@li mp_assertscope.sas
**/ **/
/* valid filter */ /* make some data */
%mp_getcols(sashelp.airline,outds=work.info) proc sql;
create table work.src(
SOME_DATETIME float format=datetime19.,
SOME_CHAR char(16),
SOME_NUM num,
SOME_TIME num format=time8.,
SOME_DATE num format=date9.
);
/* run macro, checking for scope leakage */
%mp_assertscope(SNAPSHOT)
%mp_getcols(work.src,outds=work.info)
%mp_assertscope(COMPARE)
%mp_assertdsobs(work.info, %mp_assertdsobs(work.info,
desc=Has 3 records, desc=Has 5 records,
test=EQUALS 3, test=EQUALS 5,
outds=work.test_results outds=work.test_results
) )
data work.check; data work.check;
length val $10; length val $10;
do val='NUMERIC','DATE','CHARACTER'; do val='NUMERIC','DATE','CHARACTER','DATETIME','TIME';
output; output;
end; end;
run; run;

View File

@@ -0,0 +1,231 @@
/**
@file
@brief Testing mp_loadformat.sas macro
@details first test regular formats, then informats
<h4> SAS Macros </h4>
@li mddl_dc_difftable.sas
@li mp_aligndecimal.sas
@li mp_cntlout.sas
@li mp_loadformat.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* prep format catalog */
libname perm (work);
%mddl_dc_difftable(libds=perm.audit)
/* set up regular formats */
data work.loadfmts;
/* matching start / end lengths (to baseds) are important */
length fmtname $32 start end $10000;
eexcl='Y';
type='N';
do i=1 to 10;
fmtname=cats('SASJS_',put(i,z4.),'X');
do j=1 to 20;
start=cats(j);
end=cats(j+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
label= cats('Numeric Format ',start);
output;
end;
end;
run;
proc format cntlin=work.loadfmts library=perm.testcat;
run;
/*
use actual format data as test baseline, as proc format adds attributes eg
min/max etc
*/
%mp_cntlout(libcat=perm.testcat,cntlout=work.loadfmts2)
/* make some test data */
data work.stagedata;
set work.loadfmts2 end=lastobs;
by type fmtname;
if lastobs then do;
output;
fmtname='NEWFMT'!!cats(_n_,'x'); /* 1 new record */
start=cats(_n_);
end=cats(_n_+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
label='newval'!!cats(_N_,'X');
output;
stop;
end;
else if last.fmtname then deleteme='Yes'; /* 9 deletions */
else if first.fmtname then label='modified '!!cats(_n_); /* 10 changes */
output;
run;
/* load the above */
%mp_assertscope(SNAPSHOT)
%mp_loadformat(perm.testcat
,work.stagedata
,loadtarget=YES
,auditlibds=perm.audit
,locklibds=0
,delete_col=deleteme
,outds_add=add_test1
,outds_del=del_test1
,outds_mod=mod_test1
,mdebug=1
)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_nobs(del_test1)=9),
desc=Test 1 - delete obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test1)=1),
desc=Test 1 - add obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test1)=10),
desc=Test 1 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(perm.audit)=440),
desc=Test 1 - audit table updated,
outds=work.test_results
)
data work.difftest;
set perm.audit;
where is_diff=1;
run;
%mp_assert(
iftrue=(%mf_nobs(work.difftest)>0),
desc=Test 1 - diffs were found,
outds=work.test_results
)
/* set up a mix of formats */
data work.loadfmts3;
length fmtname $32 start end $10000;
eexcl='Y';
type='J';
do i=1 to 3;
fmtname=cats('SASJS_CI_',i,'X');
do j=1 to 4;
start=cats(j);
end=start;
label= cats('Char INFORMAT ',start);
output;
end;
end;
type='I';
do i=1 to 3;
fmtname=cats('SASJS_NI_',i,'X');
do j=1 to 4;
start=cats(j);
end=cats(j+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
label= cats(ranuni(0));
output;
end;
end;
type='N';
do i=1 to 3;
fmtname=cats('SASJS_NF_',i,'X');
do j=1 to 4;
start=cats(j);
end=cats(j+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
label= cats('Numeric Format ',start);
output;
end;
end;
type='C';
do i=1 to 3;
fmtname=cats('SASJS_CF_',i,'X');
do j=1 to 4;
start=cats(j);
end=start;
label= cats('Char Format ',start);
output;
end;
end;
drop i j;
run;
proc format cntlin=work.loadfmts3 library=perm.testcat3;
run;
%mp_cntlout(libcat=perm.testcat3,cntlout=work.loadfmts4)
/* make some test data */
data work.stagedata3;
set work.loadfmts4;
where type in ('I','J');
by type fmtname notsorted;
if type='I' then do;
if last.fmtname then do;
deleteme='Yes'; /* 3 deletions */
output;
end;
else if fmtrow le 3 then do; /* 9 changed values */
z=ranuni(0)*1000000;
start=cats(z);
end=cats(z+1);
%mp_aligndecimal(start,width=16)
%mp_aligndecimal(end,width=16)
output;
end;
end;
else do;
if last.fmtname then do;
output; /* 6 new records */
x=_n_;
x+1;start=cats("mod",x);end=start;label='newlabel1';output;
x+1;start=cats("mod",x);end=start;label='newlabel2';output;
end;
else if fmtrow le 3 then do; /* 9 more changed values */
start= cats("mod",_n_);
end=start;
label= "mod "||cats(ranuni(0)*100);
output;
end;
end;
run;
%mp_loadformat(perm.testcat3
,work.stagedata3
,loadtarget=YES
,auditlibds=perm.audit
,locklibds=0
,delete_col=deleteme
,outds_add=add_test2
,outds_del=del_test2
,outds_mod=mod_test2
,mdebug=1
)
%mp_assert(
iftrue=(%mf_nobs(del_test2)=3),
desc=Test 2 - delete obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test2)=18),
desc=Test 2 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test2)=6),
desc=Test 2 - add obs,
outds=work.test_results
)

View File

@@ -0,0 +1,123 @@
/**
@file
@brief Testing mp_loadformat.sas macro for multilabel formats
@details Multilabel records can be complete duplicates!! Also, the order is
important.
The provided formats create a table as follows:
|TYPE:$1.|FMTNAME:$32.|START:$10000.|END:$10000.|LABEL:$32767.|MIN:best.|MAX:best.|DEFAULT:best.|LENGTH:best.|FUZZ:best.|PREFIX:$2.|MULT:best.|FILL:$1.|NOEDIT:best.|SEXCL:$1.|EEXCL:$1.|HLO:$13.|DECSEP:$1.|DIG3SEP:$1.|DATATYPE:$8.|LANGUAGE:$8.|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|`C `|`GENDERML `|` `|` `|`Total people `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`1 `|`1 `|`Male `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`1 `|`1 `|`Total people `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Female `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Female `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Thormale `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`C `|`GENDERML `|`2 `|`2 `|`Total people `|`1 `|`40 `|`12 `|`12 `|`0 `|` `|`0 `|` `|`0 `|`N `|`N `|`M `|` `|` `|` `|` `|
|`N `|`AGEMLA `|`1 `|`4 `|`Preschool `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLA `|`1 `|`18 `|`Children `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLA `|`19 `|`120 `|`Adults `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLB `|`1 `|`4 `|`Preschool `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLB `|`1 `|`18 `|`Children `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLB `|`19 `|`120 `|`Adults `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLC `|`1 `|`18 `|`Children `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLC `|`1 `|`4 `|`Preschool `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
|`N `|`AGEMLC `|`19 `|`120 `|`Adults `|`1 `|`40 `|`9 `|`9 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`SM `|` `|` `|` `|` `|
<h4> SAS Macros </h4>
@li mf_nobs.sas
@li mp_cntlout.sas
@li mp_loadformat.sas
@li mp_assert.sas
@li mp_assertdsobs.sas
**/
/* prep format catalog */
libname perm (work);
/* create some multilable formats */
%let cat1=perm.test1;
proc format library=&cat1;
value $genderml (multilabel notsorted)
'1'='Male'
'2'='Female'
'2'='Female'
'2'='Farmale'
'1','2',' '='Total people';
value agemla (multilabel)
1-4='Preschool'
1-18='Children'
19-120='Adults';
value agemlb (multilabel)
19-120='Adults'
1-18='Children'
1-4='Preschool';
value agemlc (multilabel notsorted)
19-120='Adults'
1-18='Children'
1-4='Preschool';
run;
%mp_cntlout(libcat=&cat1,cntlout=work.cntlout1)
%mp_assertdsobs(work.cntlout1,
desc=Has 16 records,
test=EQUALS 16
)
data work.stagedata3;
set work.cntlout1;
if fmtname='AGEMLA' and label ne 'Preschool' then deleteme='Yes';
if fmtname='AGEMLB' and label = 'Preschool' then label='Kids';
if fmtname='GENDERML' and label='Farmale' then output;
output;
run;
%mp_loadformat(&cat1
,work.stagedata3
,loadtarget=YES
,auditlibds=perm.audit
,locklibds=0
,delete_col=deleteme
,outds_add=add_test1
,outds_del=del_test1
,outds_mod=mod_test1
,mdebug=1
)
%mp_assert(
iftrue=(%mf_nobs(del_test1)=2),
desc=Test 1 - deleted obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test1)=4),
desc=Test 1 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test1)=1),
desc=Test 1 - add obs,
outds=work.test_results
)
/* now check the order of the notsorted format */
%mp_cntlout(libcat=&cat1,cntlout=work.cntlout2)
%let check1=0;
%let check2=0;
data test;
set work.cntlout2;
where fmtname='GENDERML';
if _n_=4 and label='Farmale' then call symputx('check1',1);
if _n_=5 and label='Farmale' then call symputx('check2',1);
run;
%mp_assert(
iftrue=(&check1=1 and &check2=1),
desc=Ensuring Farmale values retain their order,
outds=work.test_results
)

View File

@@ -1,93 +0,0 @@
/**
@file
@brief Testing mp_loadformat.sas macro
<h4> SAS Macros </h4>
@li mddl_dc_difftable.sas
@li mp_loadformat.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* prep format catalog */
libname perm (work);
%mddl_dc_difftable(libds=perm.audit)
data work.loadfmts;
length fmtname $32;
eexcl='Y';
type='N';
do i=1 to 100;
fmtname=cats('SASJS_',i,'X');
do j=1 to 100;
start=cats(j);
end=cats(j+1);
label= cats('Dummy ',start);
output;
end;
end;
run;
proc format cntlin=work.loadfmts library=perm.testcat;
run;
/* make some test data */
data work.stagedata;
set work.loadfmts;
type='N';
eexcl='Y';
if _n_<150 then deleteme='Yes';
else if _n_<250 then label='mod'!!cats(_n_);
else if _n_<350 then do;
start=cats(_n_);
end=cats(_n_+1);
label='newval'!!cats(_N_);
end;
else stop;
run;
/* load the above */
%mp_assertscope(SNAPSHOT)
%mp_loadformat(perm.testcat
,work.stagedata
,loadtarget=YES
,auditlibds=perm.audit
,locklibds=0
,delete_col=deleteme
,outds_add=add_test1
,outds_del=del_test1
,outds_mod=mod_test1
,mdebug=1
)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_nobs(del_test1)=149),
desc=Test 1 - delete obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test1)=100),
desc=Test 1 - add obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test1)=100),
desc=Test 1 - mod obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(perm.audit)=7329),
desc=Test 1 - audit table updated,
outds=work.test_results
)
data work.difftest;
set perm.audit;
where is_diff=1;
run;
%mp_assert(
iftrue=(%mf_nobs(work.difftest)>0),
desc=Test 1 - diffs were found,
outds=work.test_results
)

View File

@@ -4,8 +4,10 @@
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_lockanytable.sas @li mp_lockanytable.sas
@li mp_assert.sas
@li mp_assertcols.sas @li mp_assertcols.sas
@li mp_assertcolvals.sas @li mp_assertcolvals.sas
@li mp_assertscope.sas
@li mp_coretable.sas @li mp_coretable.sas
**/ **/
@@ -61,3 +63,18 @@ run;
desc=Ref is captured in unlock, desc=Ref is captured in unlock,
test=ANYVAL test=ANYVAL
) )
/* attempt unlock of a table that was never locked */
%mp_lockanytable(UNLOCK,lib=no,ds=doesnotexist,ref=bye, ctl_ds=work.controller)
%mp_assert(
iftrue=(&syscc=0),
desc=Ability to unlock a table that was never locked,
outds=work.test_results
)
/* test for macro variable scope leakage */
%mp_assertscope(SNAPSHOT)
%mp_lockanytable(LOCK,lib=tmp,ds=testscope,ref=This Ref, ctl_ds=work.controller)
%mp_assertscope(COMPARE)

View File

@@ -4,7 +4,7 @@
@brief Testing mv_jobflow macro @brief Testing mv_jobflow macro
@details One of the remote jobs aborts with syscc>0 - test to @details One of the remote jobs aborts with syscc>0 - test to
make sure this comes back to the calling session make sure this comes back to the calling session
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_assert.sas @li mp_assert.sas
@li mv_createjob.sas @li mv_createjob.sas

View File

@@ -3,7 +3,7 @@
@brief Testing mv_jobflow macro @brief Testing mv_jobflow macro
@details All jobs complete successfully with syscc = 0 - test to @details All jobs complete successfully with syscc = 0 - test to
make sure this comes back to the calling session make sure this comes back to the calling session
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_assert.sas @li mp_assert.sas
@li mv_createjob.sas @li mv_createjob.sas

View File

@@ -4,7 +4,7 @@
@brief Testing mv_registerclient.sas macro @brief Testing mv_registerclient.sas macro
@details Tests for successful registration. For this to work, the test @details Tests for successful registration. For this to work, the test
account must be an admin. account must be an admin.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mp_assertcolvals.sas @li mp_assertcolvals.sas

View File

@@ -1,10 +1,9 @@
/** /**
@file @file
@brief Testing mv_registerclient.sas macro @brief Testing mv_registerclient.sas macro
@details Tests for unsuccessful registration. To do this, overrides are @details Tests for unsuccessful registration. To do this, overrides are
applied for the mf_loc.sas and mp_abort.sas macros. applied for the mf_loc.sas and mp_abort.sas macros.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_assert.sas @li mp_assert.sas
@li mv_registerclient.sas @li mv_registerclient.sas

View File

@@ -1,60 +1,60 @@
/** /**
@file mfv_existsashdat.sas @file mfv_existsashdat.sas
@brief Checks whether a CAS sashdat dataset exists in persistent storage. @brief Checks whether a CAS sashdat dataset exists in persistent storage.
@details Can be used in open code, eg as follows: @details Can be used in open code, eg as follows:
%if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!; %if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!;
The function uses `dosubl()` to run the `table.fileinfo` action, for the The function uses `dosubl()` to run the `table.fileinfo` action, for the
specified library, filtering for `*.sashdat` tables. The results are stored specified library, filtering for `*.sashdat` tables. The results are stored
in a WORK table (&outprefix._&lib). If that table already exists, it is in a WORK table (&outprefix._&lib). If that table already exists, it is
queried instead, to avoid the dosubl() performance hit. queried instead, to avoid the dosubl() performance hit.
To force a rescan, just use a new `&outprefix` value, or delete the table(s) To force a rescan, just use a new `&outprefix` value, or delete the table(s)
before running the function. before running the function.
@param libds library.dataset @param libds library.dataset
@param outprefix= (work.mfv_existsashdat) Used to store the current HDATA @param outprefix= (work.mfv_existsashdat) Used to store the current HDATA
tables to improve subsequent query performance. This reference is a prefix tables to improve subsequent query performance. This reference is a prefix
and is converted to `&prefix._{libref}` and is converted to `&prefix._{libref}`
@return output returns 1 or 0 @return output returns 1 or 0
@version 0.2 @version 0.2
@author Mathieu Blauw @author Mathieu Blauw
**/ **/
%macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat %macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat
); );
%local rc dsid name lib ds; %local rc dsid name lib ds;
%let lib=%upcase(%scan(&libds,1,'.')); %let lib=%upcase(%scan(&libds,1,'.'));
%let ds=%upcase(%scan(&libds,-1,'.')); %let ds=%upcase(%scan(&libds,-1,'.'));
/* if table does not exist, create it */ /* if table does not exist, create it */
%if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do; %if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do;
%let rc=%sysfunc(dosubl(%nrstr( %let rc=%sysfunc(dosubl(%nrstr(
/* Read in table list (once per &lib per session) */ /* Read in table list (once per &lib per session) */
proc cas; proc cas;
table.fileinfo result=source_list /caslib="&lib"; table.fileinfo result=source_list /caslib="&lib";
val=findtable(source_list); val=findtable(source_list);
saveresult val dataout=&outprefix._&lib; saveresult val dataout=&outprefix._&lib;
quit; quit;
/* Only keep name, without file extension */ /* Only keep name, without file extension */
data &outprefix._&lib; data &outprefix._&lib;
set &outprefix._&lib(where=(Name like '%.sashdat') keep=Name); set &outprefix._&lib(where=(Name like '%.sashdat') keep=Name);
Name=upcase(scan(Name,1,'.')); Name=upcase(scan(Name,1,'.'));
run; run;
))); )));
%end; %end;
/* Scan table for hdat existence */ /* Scan table for hdat existence */
%let dsid=%sysfunc(open(&outprefix._&lib(where=(name="&ds")))); %let dsid=%sysfunc(open(&outprefix._&lib(where=(name="&ds"))));
%syscall set(dsid); %syscall set(dsid);
%let rc = %sysfunc(fetch(&dsid)); %let rc = %sysfunc(fetch(&dsid));
%let rc = %sysfunc(close(&dsid)); %let rc = %sysfunc(close(&dsid));
/* Return result */ /* Return result */
%if "%trim(&name)"="%trim(&ds)" %then 1; %if "%trim(&name)"="%trim(&ds)" %then 1;
%else 0; %else 0;
%mend mfv_existsashdat; %mend mfv_existsashdat;

View File

@@ -761,6 +761,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) '; put ' prxchange(''s/\\/\\\\/'',-1,syserrortext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syserrortext=cats(''"'',syserrortext,''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
@@ -791,6 +792,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) '; put ' prxchange(''s/\\/\\\\/'',-1,syswarningtext) ';
put ' )))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else syswarningtext=cats(''"'',syswarningtext,''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; '; put ' length memsize $32; ';

View File

@@ -222,6 +222,7 @@
prxchange('s/\\/\\\\/',-1,syserrortext) prxchange('s/\\/\\\\/',-1,syserrortext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syserrortext=cats('"',syserrortext,'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
@@ -252,6 +253,7 @@
prxchange('s/\\/\\\\/',-1,syswarningtext) prxchange('s/\\/\\\\/',-1,syswarningtext)
)))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else syswarningtext=cats('"',syswarningtext,'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length memsize $32; length memsize $32;