Translate

2024년 1월 30일 화요일

[Java] jxl 라이브러리 팁


Laptop
운영체제 Windows 10 Pro 64bit
개발환경
JDK1.7.0_80

현 시점에서는 상당히 오래전에 쓰던 엑셀 라이브러리지만,
유지보수 프로젝트 참여 중 기능을 추가해보면서 알게 된 내용을 정리해본다.

일단 기본적인 개념으로, jxl은 읽기 객체와 쓰기 객체가 따로 존재한다.
예를 들어 아래와 같은 소스가 있을 때,

Workbook originExcel = Workbook.getWorkbook(path);
WritableWorkbook newExcel = Workbook.createWorkbook(file, originExcel);

Workbook 클래스로 생성된 originExcel는 읽기 전용 객체가 되고,
.getSheet, .getCell 메소드로 얻는 Sheet, Cell 객체들 또한 읽기 전용 객체가 되며, 즉 원본 파일(path)에 대해 엑셀 객체를 생성하여 내용을 읽을 수는 있지만 직접적으로 파일에 영향을 줄 수는 없다는 뜻이다.

그러나 WritableWorkbook 클래스로 생성된 newExcel은 쓰기 가능 객체로,
.getSheet, .getWritableCell 메소드를 통해 WritableSheet, WritableCell 라는 셀의 추가나 수정 등을 할 수 있는, 즉 파일에 영향을 줄 수 있는 객체를 얻을 수 있게 된다.

정리하면 위 상태는 원본 파일(String path)로부터 엑셀 객체를 생성한 뒤,
그것의 복사 파일(File file)을 만들어 수정 가능 상태로 만들어놓은 설정이라고 할 수 있다.

그럼 이제 원본 파일의 경우 아무 내용도 없는 템플릿으로 만들어두고,
복사 파일에서는 프로그래밍적으로 값을 입력하면 될텐데, 여기서 치명적인 문제가 하나 있다.
셀의 스타일 등을 유지한 채 값만 바꾸는, 즉 비교적 최신 엑셀 라이브러리인 poi 라이브러리의 setCellValue에 해당하는 기능이 jxl에는 없다.
WritableCell 객체에도 setCellFormat과 같이 속성을 바꾸는 메소드는 있지만 값을 바꾸는 메소드가 없다.

WritableCellFormat를 생성해서 일일히 설정하여 셀을 생성하는 것도 방법이지만,
그렇게 되면 굳이 앞에서 복사본을 생성한 이유가 없어진다.
그래서 셀을 생성하되, 원래 셀의 속성을 가져와 사용하는 방식을 사용하면 된다.

WritableSheet sheet = newExcel.getSheet(0);
for (int r = 0; r < sheet.getRows(); r++) {
	for (int c = 0; c < sheet.getColumns(); c++) {
		CellFormat format = sheet.getCell(c, r).getCellFormat();
		Label cell = new Label(c, r, "값", format);
		sheet.addCell(cell);
	}
}

이렇게 만들어진 셀에서는 cell.setString("값2"); 를 통해 값 수정도 가능하다.


예시로 Label을 썼는데, 이것은 문자열 값을 받는 셀 객체를 의미하기 때문에
숫자에 쉼표를 넣는 등의 설정이 필요할 경우 java에서 구현해서 넣어야 하는 비효율적인 문제가 발생한다.

좀 더 괜찮은 방법으로는 원본 엑셀에 해당하는 부분은 셀 서식에서 숫자를 설정해준 뒤,  
위 소스에서 Label 부분을
Number cell = new Number(c, r, 100, format);
이렇게 전환하면 숫자 설정을 유지할 수 있게 된다.


또한 수식을 사용하고 싶으면 Formula 객체를 이용하면 되는데,
주의사항은 jxl에서 사용하는 index의 개념은 0부터 시작하지만,
실제 엑셀 파일에서 사용되는 값은 행은 1부터 시작되는 순번, 열은 알파벳으로 인식되기 때문에
이 부분에 주의해서 수식을 입력해야 한다.

아래는 getColNm 메소드를 통해 index를 알파벳으로 치환하여,
c가 0이라는 전제에서 =SUM(A1:A10) 수식 셀을 생성하는 예시이다.

int start = 0;
int end = 9;
String nm = getColNm(c);
Formula cell = new Formula(c, r, "SUM(" + nm + (start + 1) + ":" + nm + (end + 1) + ")", format);

...

private String getColNm(int columnNumber) {
	StringBuilder columnName = new StringBuilder();
	columnNumber++;
	while (columnNumber > 0) {
		int rem = columnNumber % 26;
		if (rem == 0) {
			columnName.insert(0, 'Z');
			columnNumber = (columnNumber / 26) - 1;
		} else {
			columnName.insert(0, (char) ((rem - 1) + 'A'));
			columnNumber = columnNumber / 26;
		}
	}
	return columnName.toString();
}

추가로 jxl-2.6.12 버전에서 SUM와 같은 엑셀 함수 사용 시
Can't find bundle for base name functions ...
와 같은 오류를 발생시키는 경우가 있는데,
이는 특정 시기에 배포 오류가 있었던건지 jxl 파일 내에 관련 파일이 없는 경우가 있다.
기존에 구축된 프로젝트라 버전이나 라이브러리를 바꾸는데 부담이 있다면, 
새로운 버전을 jar로 임시로 받아 압축 프로그램을 사용하여 functions.properties 파일만 추출해서 기존 파일에 넣으면 정상적으로 동작한다.
(이름이 비슷한 다른 파일들은 언어별 파일인것 같은데, 맞는 언어가 없어도 자동적으로 기본 파일만 참조하는 것 같다.)


Formula로 생성한 셀의 수식은 cell.getContents()로 조회 가능하나
수식 결과로 나온 값은 java에서는 조회가 불가능하며,
읽기 전용인 원본 파일에서 수식이 입력되어있던 셀은 수식, 결과 모두 조회 불가능하다.

마지막으로 아무 값도 없이 속성만 복사한 셀을 생성할 때는
Blank cell = new Blank(c, r, format);
이렇게 하면 된다.
(원본 파일에 있던 셀인 경우 겉보기에는 굳이 java에서 건드리는 의미는 없으나, 엑셀 함수 사용 시 범위에 포함된 셀을 Blank로 생성했을 경우 0으로 인식되지만 그대로 둔 셀은 값 오류가 발생하는 경우가 있었다.)

참고로 Label, Number, Formula, Blank 모두 WritableCell 인터페이스의 구현 클래스들이므로
위의 예시들에서 WritableCell cell = ... 로 선언해도 상관없으나,
setString(Number의 경우 setValue)과 같이 나중에 값을 수정할 수 없다는 단점은 있으나
셀 타입마다 일일히 다른 cell 객체를 생성하지 않아도 된다는 장점이 있다.

2024년 1월 26일 금요일

[Python] Window에서 Python 설치한 상태로 pip 없을 때 설치법


Laptop
운영체제 Windows 10 Pro 64bit


Python은 설치했으나 어떠한 이유로 pip이 설치되지 않았을 때,
즉 Python설치경로/Scripts 안에 pip.exe가 존재하지 않을 때 cmd에서 아래의 커맨드를 입력하면 설치가 된다.

> py -m ensurepip --default-pip

2024년 1월 24일 수요일

[Eclipse] SVN, GIT에서 공백 자동 생성되는 현상 해결 정리



  


주로 다른 개발자가 만든 소스를 전달받았을 때, 
위와 같이 육안으로 보이지 않는 공백이 버전관리 툴에서 잡히는 경우들과 해결책을 정리해보았다.


1) UTF-8 BOM

파일 앞부분에 특정 문자를 넣는 방식이라 첫번째 줄에서 차이점이 발견되며, 이클립스 상에서 파일 우측메뉴 Properties - Resource 에서 확인할 수 있다.


.java 파일일 경우 빌드 시 illegal character: '\ufeff' 라는 오류를 발생시키기도 한다.

해결 방법은 VSCode, Notepad++ 등 인코딩 전환을 지원하는 에디터를 통해 다시 저장해주면 된다.

- Notepad++
상단 인코딩 메뉴에서 변경 후 저장(Ctrl + S)




- VSCode
1. 하단 인코딩 표시 영역 클릭


2. 커맨드 창에서 Save with Encoding 클릭



3. UTF-8 클릭



2) GIT autocrlf

윈도우와 리눅스 간 개행문자의 차이로 발생하는 문제로, 
이를 자동으로 전환해주는 옵션으로 이클립스 Window - Preferences 에서 설정 가능하다.

개발자들이 모두 윈도우에서만 작업하는 경우 true, false 모두 문제가 있지는 않지만
설정이 다 제각각인 상태에서는 전환이 발생하여 차이점으로 인식될 수 있으므로
심플하게 false로 통일하거나 각각의 상황에 맞추어 변경하면 될 것 같다. 



3) AnyEdit 설정

이클립스 확장 프로그램 중 다양한 기능을 제공하는 AnyEdit이라는 플러그인을 설치하면,
Remove trailing whitespace라는 저장할 때 의미없는 공백을 제거하는 옵션이 기본적으로 적용되도록 설정되어 있어 의도치 않은 수정이 생기는 현상이 발생할 수 있다.
Window - Preferences 에서 해제하면 된다.





- 참고 사이트:

2022년 4월 1일 금요일

[넥사크로, Nexacro 14] 그리드 값 수정 여부 표시


Laptop
운영체제 Windows 10 Pro 64bit
개발환경 nexacro studio 14, 0, 1, 3900




유효성이 중요한 데이터를 다루던 중 데이터 수정 및 신규 여부를 화면에 나타내야 해서 만든 기능이다. 

넥사크로의 Grid에는 ROWTYPE이라는 값 수정 여부를 인식하는 속성이 있지만 값이 변경되는 시점이 edit 컴포넌트마다 달라서 이벤트를 다르게 써야 한다.

TEXT에서 입력 취소 기능(타이핑 도중 포커스 아웃하면 값 복원)은 편의상 넣었는데 이것도 역시 key 이벤트 타이밍을 잘 고려해야 한다.

Source
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?xml version="1.0" encoding="utf-8"?>
<FDL version="1.5">
  <TypeDefinition url="..\default_typedef.xml"/>
  <Form id="test" left="0" top="0" width="500" height="300" titletext="New Form" onload="test_onload">
    <Layouts>
      <Layout>
        <Grid id="Grid00" taborder="0" binddataset="dsGrid" autofittype="col" onkeydown="Grid00_onkeydown" onkeyup="Grid00_onkeyup" onselectchanged="Grid00_onselectchanged" oncloseup="Grid00_oncloseup" ontextchanged="Grid00_ontextchanged" left="75" top="68" width="350" height="100">
          <Formats>
            <Format id="default">
              <Columns>
                <Column size="40"/>
                <Column size="100"/>
                <Column size="80"/>
                <Column size="60"/>
              </Columns>
              <Rows>
                <Row size="24" band="head"/>
                <Row size="24"/>
              </Rows>
              <Band id="head">
                <Cell text="GBN"/>
                <Cell col="1" text="TEXT"/>
                <Cell col="2" text="DATE"/>
                <Cell col="3" text="COMBO"/>
              </Band>
              <Band id="body">
                <Cell style="align:center;color:red;color2:red;selectcolor:red;" text="bind:GBN"/>
                <Cell col="1" edittype="text" style="align:center;" text="bind:TEXT"/>
                <Cell col="2" displaytype="date" edittype="date" style="align:center;" text="bind:DATE"/>
                <Cell col="3" displaytype="combo" edittype="combo" style="align:center;" text="bind:COMBO" combodataset="dsCombo" combocodecol="CODE" combodatacol="VALUE"/>
              </Band>
            </Format>
          </Formats>
        </Grid>
        <Button id="btnAdd" taborder="1" text="ADD" onclick="btnAdd_onclick" left="140" top="183" width="60" height="30"/>
        <Button id="btnReset" taborder="2" text="RESET" onclick="btnReset_onclick" left="220" top="183" width="60" height="30"/>
        <Button id="btnSave" taborder="3" text="SAVE" onclick="btnSave_onclick" left="300" top="183" width="60" height="30"/>
      </Layout>
    </Layouts>
    <Objects>
      <Dataset id="dsGridCopy"/>
      <Dataset id="dsGrid">
        <ColumnInfo>
          <Column id="GBN" size="1" prop="default" type="STRING"/>
          <Column id="TEXT" size="10" prop="default" type="STRING"/>
          <Column id="DATE" prop="default" type="STRING"/>
          <Column id="COMBO" prop="default" type="INT"/>
        </ColumnInfo>
        <Rows>
          <Row>
            <Col id="GBN"/>
            <Col id="TEXT">TEST</Col>
            <Col id="DATE">20220101</Col>
            <Col id="COMBO">1</Col>
          </Row>
        </Rows>
      </Dataset>
      <Dataset id="dsCombo">
        <ColumnInfo>
          <Column id="CODE" prop="default" type="INT"/>
          <Column id="VALUE" size="10" prop="default" type="STRING"/>
        </ColumnInfo>
        <Rows>
          <Row>
            <Col id="CODE">1</Col>
            <Col id="VALUE">1</Col>
          </Row>
          <Row>
            <Col id="CODE">2</Col>
            <Col id="VALUE">2</Col>
          </Row>
          <Row>
            <Col id="CODE">3</Col>
            <Col id="VALUE">3</Col>
          </Row>
        </Rows>
      </Dataset>
    </Objects>
  </Form>
</FDL>

Script
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
this.isInput = false;  // 입력 중 상태 여부
this.inputBackup = '';  // 입력하기 전 값 백업

this.test_onload = function(obj:Form, e:nexacro.LoadEventInfo)
{
  this.dsGridCopy.copyData(this.dsGrid);  // 초기 데이터 백업
}

this.Grid00_ontextchanged = function(obj:Grid, e:nexacro.GridEditTextChangedEventInfo)
{
  // 수정 전 값 백업
  if (!this.isInput) {
    this.inputBackup = e.pretext;
  }
  if (this.dsGrid.getColumn(e.row, "GBN") != 'C') {
    this.isInput = true;
    this.dsGrid.setColumn(e.row, "GBN", 'U');
  }
}

this.Grid00_onkeyup = function(obj:Grid, e:nexacro.KeyEventInfo)
{
  var col = obj.getCellProperty("Body", obj.currentcol, "text").split(':')[1]; // 컬럼명
  // Enter 눌러서 입력 완료 후 ROWTYPE 상태에 따라 GBN 표시 (U: 수정, C: 신규)
  if (this.isInput && e.keycode == nexacro.Event.KEY_ENTER) {
    this.isInput = false;
    this.dsGrid.setColumn(obj.currentrow, "GBN", '');
    if (this.dsGrid.getRowType(obj.currentrow) == Dataset.ROWTYPE_UPDATE) {
      this.dsGrid.setColumn(obj.currentrow, "GBN", 'U');
    }
  }
}

this.Grid00_onkeydown = function(obj:Grid, e:nexacro.KeyEventInfo)
{
  if(e.keycode == nexacro.Event.KEY_ESC) { // 입력 도중 ESC 누를 경우
    // 포커스 해제
    obj.set_enable(false);
    obj.set_enable(true);
    
    var col = obj.getCellProperty("Body", obj.currentcol, "text").split(':')[1];
    if (this.isInput) {
      // 수정 전 값으로 복구
      this.dsGrid.setColumn(obj.currentrow, col, this.inputBackup);
      this.dsGrid.setColumn(obj.currentrow, "GBN", '');
      if (this.dsGrid.getRowType(obj.currentrow) == Dataset.ROWTYPE_UPDATE) {
        this.dsGrid.setColumn(obj.currentrow, "GBN", 'U');
      }
    }
      
    this.isInput = false;
  }
}

this.Grid00_onselectchanged = function(obj:Grid, e:nexacro.GridSelectEventInfo)
{ // 다른 row를 선택했을 경우 (입력 종료로 간주)
  var col = obj.getCellProperty("Body", e.cell, "text").split(':')[1];

  if (this.isInput && obj.getCellValue(e.oldrow, e.oldcol) == this.dsGridCopy.getColumn(e.oldrow, e.oldcol))
    this.dsGrid.setColumn(e.oldrow, "GBN", 0);
  this.isInput = false;
}

this.Grid00_oncloseup = function(obj:Grid, e:nexacro.GridEditEventInfo)
{ // 날짜, 콤보박스 선택 후
  var col = obj.getCellProperty("Body", e.cell, "text").split(':')[1];
    
  this.dsGrid.setColumn(e.row, "GBN", '');
  if (this.dsGrid.getRowType(e.row) == Dataset.ROWTYPE_UPDATE ||
    (e.value != this.dsGridCopy.getColumn(e.row, col) && this.dsGrid.getColumn(e.row, "GBN") != 'C')
      ) {
    this.dsGrid.setColumn(e.row, "GBN", 'U');
  }
}

this.btnAdd_onclick = function(obj:Button, e:nexacro.ClickEventInfo)
{ // 신규 데이터 추가
  var row = this.dsGrid.addRow();
  this.dsGrid.setColumn(row, 'GBN', 'C');
}

this.btnReset_onclick = function(obj:Button, e:nexacro.ClickEventInfo)
{ // 초기화
  this.dsGrid.reset();
}

this.btnSave_onclick = function(obj:Button, e:nexacro.ClickEventInfo)
{ // 저장 (전체 GBN ''로 설정 후 Dataset 적용 및 Copy 재처리)
  for (var i=0; i < this.dsGrid.getRowCount(); i++) {
    this.dsGrid.setColumn(i, "GBN", '');
  }
  this.dsGrid.applyChange();
  this.dsGridCopy.copyData(this.dsGrid);
}

2022년 3월 22일 화요일

유용한 프로그램 모음


2022년 3월 18일 금요일

[Oracle] 오라클 시스템 객체 쿼리 모음


Laptop
운영체제Windows 10 Pro 64bit
개발환경Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production


1. 테이블 DML 쿼리 Lock 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SELECT DISTINCT
       T1.SESSION_ID
     , T2.SERIAL#
     , T4.OBJECT_NAME
     , T2.MACHINE
     , T2.TERMINAL
     , T2.PROGRAM
     , T3.ADDRESS
     , T3.PIECE
     , T3.SQL_TEXT
     , T2.PREV_EXEC_START
     , 'ALTER SYSTEM KILL SESSION ' || '''' || T1.SESSION_ID || ', ' || T2.SERIAL# || ''';' KILL_SQL
     , T5.SPID PID
  FROM V$LOCKED_OBJECT T1
     , V$SESSION T2
     , V$SQLTEXT T3
     , DBA_OBJECTS T4
     , V$PROCESS T5
 WHERE T1.SESSION_ID = T2.SID
   AND T1.OBJECT_ID = T4.OBJECT_ID
   AND T2.SQL_ADDRESS = T3.ADDRESS
   AND T2.PADDR = T5.ADDR
--   AND MACHINE = 'PC명'
--   AND OBJECT_NAME = '테이블명'
   ORDER BY T3.ADDRESS, T3.PIECE;


2.  Package DDL Lock 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SELECT S.SID, S.SERIAL#,
       L.LOCK_TYPE,
       L.MODE_HELD,
       L.MODE_REQUESTED,
       L.LOCK_ID1,
       'ALTER SYSTEM KILL SESSION ' || '''' || S.SID || ', ' || S.SERIAL#, || ''';' KILL_SQL
    FROM   DBA_LOCK_INTERNAL L,
       V$SESSION S
    WHERE S.SID = L.SESSION_ID
    AND L.LOCK_TYPE = 'Body Definition Lock'
    AND UPPER(L.LOCK_ID1) = UPPER('패키지명');


3. JOB 목록 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT
  JOB,
  WHAT,
  INTERVAL,
  TO_CHAR(NEXT_DATE, 'YYYY-MM-DD HH24:MI:SS') NEXT_DATE,
  TO_CHAR(THIS_DATE, 'YYYY-MM-DD HH24:MI:SS') THIS_DATE
FROM
  USER_JOBS
WHERE
  BROKEN = 'N'
  AND what LIKE '%' || 'JOB명' || '%'
ORDER BY
  NEXT_DATE;


4. 실행 중인 JOB 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT D.JOB,
       S.SID,
       S.SERIAL#,
       STATUS,
       LOG_USER USERNAME,
       WHAT,
       DECODE (TRUNC (SYSDATE - LOGON_TIME), 0, NULL, 
            TRUNC (SYSDATE - LOGON_TIME) || ' Days' || ' + ') || TO_CHAR (TO_DATE(TRUNC(MOD(SYSDATE - LOGON_TIME, 1) * 86400), 'SSSSS'), 'HH24:MI:SS')
         RUNNING,
       D.FAILURES,
       'ALTER SYSTEM KILL SESSION ' || '''' || S.SID || ', ' || S.SERIAL# || ''';' KILL_SQL
  FROM DBA_JOBS_RUNNING D, V$SESSION S, DBA_JOBS J
 WHERE S.SID = D.SID AND D.JOB = J.JOB;


5. 객체(Package, Procedure, Function, Trigger...) 스크립트 검색

1
2
3
4
5
SELECT * FROM
  USER_SOURCE
WHERE
  UPPER('%' || '검색어' || '%')
  AND NAME LIKE '%' || '객체명' || '%';

단, 위 쿼리에서 VIEW는 USER_VIEWS 라는 별도의 테이블을 쓰기 때문에 조회되지 않는데, 스크립트가 나오는 TEXT 컬럼의 타입이 LONG으로 되어있어서 LIKE문을 쓸 수 없기 때문에 만약 함께 포함된 결과를 보고싶다면 CLOB 타입으로 변환할 임시 테이블을 쓰는 약간 번거로운 과정을 거쳐야 한다.
또한 LINE의 경우 VIEW 스크립트에서는 CREATE 부분을 없앤 상태로 주 쿼리만을 보여주기 때문에 developer 등의 기능으로 조회해서 볼 경우 맞지 않아보일 수 있다.

편하게 쓰기 위해 프로시저로 정의했다.
(임시 테이블이 남는게 싫다면 프로시저 내에서 EXECUTE로 CREATE/DROP을 할 수도 있을 것 같은데, DDL이 너무 자주 수행되는 것도 낭비같아서 일단은 남겨두기로...)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
CREATE GLOBAL TEMPORARY TABLE USER_VIEWS_CLOB
(
  NAME  VARCHAR2(30 BYTE),
  TEXT  CLOB
);

CREATE OR REPLACE PROCEDURE SP_SEARCH_OBJ(
    p_text IN VARCHAR2,
    p_type IN VARCHAR2,
    p_name IN VARCHAR2,
    v_result OUT SYS_REFCURSOR
)
IS 
BEGIN
    INSERT INTO USER_VIEWS_CLOB
    SELECT VIEW_NAME, TO_LOB(TEXT) FROM USER_VIEWS;
    
    OPEN v_result FOR
        SELECT * FROM (
            SELECT
              NAME, TYPE, LINE, TEXT
            FROM
              USER_SOURCE
            UNION
            SELECT
              NAME, 'VIEW' TYPE, LINE, TO_CHAR(TEXT)
            FROM
              (
                SELECT
                  NAME, LEVEL LINE, REGEXP_SUBSTR (TEXT, '.+', 1, LEVEL) TEXT
                FROM (
                    SELECT
                      NAME, TEXT
                    FROM
                      USER_VIEWS_CLOB
                    WHERE
                      UPPER(text) LIKE UPPER('%' || p_text || '%')
                  ) CONNECT BY LEVEL <= REGEXP_COUNT (TEXT, '.+')
              )
        )
        WHERE 
            UPPER(text) LIKE UPPER('%' || p_text || '%')
            AND TYPE = NVL(UPPER(p_type), TYPE)
            AND NAME LIKE '%' || UPPER(p_name) || '%';
    
    DELETE FROM  USER_VIEWS_CLOB;
    COMMIT;
END;

-- EXEC SP_SEARCH_OBJ('검색어', '타입', '객체명', :v_result);


6. 임의 날짜로부터 특정 일수동안의 목록 조회

ex) 2022년 1월 1일부터 1년(365일)
1
2
3
SELECT TO_DATE('20220101','YYYYMMDD') + (LEVEL-1) AS v_date
FROM DUAL 
CONNECT BY LEVEL <= (365)

~ 



- 참고 사이트




2021년 3월 18일 목요일

[Eclipse, JSTL] 이클립스 정규 표현식을 이용한 Find/Replace로 웹 취약점 XSS 조치 (${} -> c:out 변환)





(\$\{.+?\})

<c:out value='\1'/>

웹 취약점 조치 작업중 단축키 Ctrl+F 로 나오는 Find/Replace에서 ${abc} -> <c:out value='${abc}'/> 로 일괄 변환할 수 있는 정규 표현식이다.

또한 이미 기존 JSTL태그에서 사용된것도 구분없이 변환되므로 위 바꾸기를 한 후에
<c:out value=.<c:out value
이런식으로 중복처리된 데이터를 탐색할 수 있다.

Regular Expression는 반드시 체크해야함