>

SQLite 쿼리에서 변수 값을 사용하기위한 표준 접근 방식은 "물음표 스타일"입니다.

import sqlite3
with sqlite3.connect(":memory:") as connection:
    connection.execute("CREATE TABLE foo(bar)")
    connection.execute("INSERT INTO foo(bar) VALUES (?)", ("cow",))
    print(list(connection.execute("SELECT * from foo")))
    # prints [(u'cow',)]

그러나 이는 값을 쿼리로 대체하는 경우에만 작동합니다. 테이블 또는 열 이름에 사용될 때 실패합니다 :

import sqlite3
with sqlite3.connect(":memory:") as connection:
    connection.execute("CREATE TABLE foo(?)", ("bar",))
    # raises sqlite3.OperationalError: near "?": syntax error

와이즈 비즈  모듈이나 PEP 249 는 이름이나 값을 이스케이프 처리하는 기능을 언급합니다. 아마도 이것은 사용자가 쿼리를 문자열로 어셈블하지 못하게하는 것입니다. 그러나 그것은 나를 잃어 버립니다.

SQLite에서 열 또는 테이블에 변수 이름을 사용하는 데 가장 적합한 함수 또는 기술은 무엇입니까? 필자는 자신의 래퍼에서 사용할 것이기 때문에 다른 종속성 없이이 작업을 수행하는 것이 좋습니다.

내 자신의 함수를 작성하는 데 사용하기 위해 SQLite 구문의 관련 부분에 대한 명확하고 완전한 설명을 찾을 수 없었습니다. SQLite에서 허용하는 모든 식별자에 이것이 작동하기를 원하므로 시행 착오 솔루션이 너무 확실하지 않습니다.

SQLite sqlite3 사용  식별자를 인용 하지만 이스케이프만으로 충분하다는 확신이 없습니다. PHP의 " 함수의 문서에 따르면 특정 이진 데이터도 이스케이프해야 할 수도 있지만 PHP 라이브러리의 단점 일 수도 있습니다.

sqlite_escape_string

  • 답변 # 1

    문자열을 SQLite 식별자로 변환하려면 :

    문자열을 UTF-8로 인코딩 할 수 있는지 확인하십시오.

    문자열에 NUL 문자가 포함되지 않도록하십시오.

    " 모두 교체   "" 와 함께 .

    전체 내용을 큰 따옴표로 묶습니다.

    구현
    import codecs
    def quote_identifier(s, errors="strict"):
        encodable = s.encode("utf-8", errors).decode("utf-8")
        nul_index = encodable.find("\x00")
        if nul_index >= 0:
            error = UnicodeEncodeError("NUL-terminated utf-8", encodable,
                                       nul_index, nul_index + 1, "NUL not allowed")
            error_handler = codecs.lookup_error(errors)
            replacement, _ = error_handler(error)
            encodable = encodable.replace("\x00", replacement)
        return "\"" + encodable.replace("\"", "\"\"") + "\""
    
    

    문자열 단일 인수를 주면 올바르게 이스케이프하여 인용하거나 예외를 발생시킵니다. 두 번째 인수는 codecs 에 등록 된 오류 핸들러를 지정하는 데 사용할 수 있습니다.  구성 단위. 내장 된 것들 :

    와이즈 비즈 와이즈 비즈 : 인코딩 오류 발생시 예외 발생

      

      : 잘못된 데이터를 'strict' 와 같은 적절한 대체 마커로 바꿉니다.  또는 'replace'

        

      '?' : 잘못된 형식의 데이터를 무시하고 추가 통지없이 계속 진행

        

      '\ufffd' : 적절한 XML 문자 참조로 교체 (인코딩 전용)

        

      'ignore' : 백 슬래시 이스케이프 시퀀스로 교체 (인코딩에만 해당)

        

    예약 된 식별자를 확인하지 않으므로 새로운 'xmlcharrefreplace' 를 만들려고하면  당신을 멈추지 않을 것입니다.

    사용 예
    'backslashreplace'
    
    
    관찰과 참고 문헌

    SQLite 식별자는 SQLITE_MASTER 입니다 이진이 아닙니다.

    import sqlite3 def test_identifier(identifier): "Tests an identifier to ensure it's handled properly." with sqlite3.connect(":memory:") as c: c.execute("CREATE TABLE " + quote_identifier(identifier) + " (foo)") assert identifier == c.execute("SELECT name FROM SQLITE_MASTER").fetchone()[0] test_identifier("'Héllo?'\\\n\r\t\"Hello!\" -☃") # works test_identifier("北方话") # works test_identifier(chr(0x20000)) # works print(quote_identifier("Fo\x00o!", "replace")) # prints "Fo?o!" print(quote_identifier("Fo\x00o!", "ignore")) # prints "Foo!" print(quote_identifier("Fo\x00o!")) # raises UnicodeEncodeError print(quote_identifier(chr(0xD800))) # raises UnicodeEncodeError FAQ의 스키마

    Python 2 SQLite API는 텍스트로 디코딩 할 수없는 바이트를 주었을 때 소리 쳤다.

    Python 3 SQLite API는 쿼리가 TEXT 여야합니다. SQLITE_MASTER 가 아닌 s .

    SQLite 식별자는 큰 따옴표를 사용하여 인용됩니다.

    SQLite가 이해 한 SQL

    SQLite 식별자의 큰 따옴표는 두 개의 큰 따옴표로 이스케이프됩니다.

    SQLite 식별자는 대소 문자를 유지하지만 ASCII 문자는 대소 문자를 구분하지 않습니다. 유니 코드 인식 대/소문자 구분을 활성화 할 수 있습니다.

    SQLite FAQ 질문 # 18

    SQLite는 문자열 또는 식별자의 NUL 문자를 지원하지 않습니다.

    SQLite 티켓 57c971fc74

    str  UTF-8로 올바르게 인코딩 될 수있는 한 다른 모든 유니 코드 문자열을 처리 할 수 ​​있습니다. 잘못된 문자열은 Python 3.0과 Python 3.1.2 또는 그 주변에서 충돌을 일으킬 수 있습니다. 파이썬 2는이 잘못된 문자열을 받아 들였지만 이것은 버그로 간주됩니다.

    Python Issue # 12569

    모듈 /_sqlite/cursor.c

    다발 테스트를 거쳤습니다.

  • 답변 # 2

    와이즈 비즈  문서에서는 테이블 및 열 이름 (또는 다른 비트의 동적 구문)을 대체하기 위해 일반 파이썬 % 또는 {} 형식을 사용한 다음 매개 변수 메커니즘을 사용하여 값을 쿼리에 대체하도록 권장합니다.

    저는 "동적 테이블/열 이름을 사용하지 마십시오. 필요한 경우 잘못된 일을하고 있습니다"라고 말하는 모든 사람에 동의하지 않습니다. 나는 매일 데이터베이스로 물건을 자동화하는 프로그램을 작성하고 그것을 항상한다. 우리는 많은 테이블을 가진 많은 데이터베이스를 가지고 있지만, 그것들은 모두 반복 된 패턴을 기반으로하므로이를 처리하는 일반적인 코드는매우유용합니다. 매번 쿼리를 직접 작성하면 오류가 발생하기 쉽고 훨씬 위험합니다.

    "안전한"의 의미로 귀착됩니다. 일반적인 지혜는 일반적인 파이썬 문자열 조작을 사용하여 쿼리에 값을 넣는 것이 "안전하지 않다"는 것입니다. 그럴 경우 잘못 될 수있는 모든 종류의 것들이 있기 때문에 그러한 데이터는 사용자가 자주 가져 오며 사용자가 통제 할 수 없기 때문입니다. 사용자가 데이터 값에 SQL을 삽입하고 데이터베이스에서 실행할 수 없도록 이러한 값을 올바르게 이스케이프하는 100 % 안정적인 방법이 필요합니다. 그래서 도서관 작가들은이 일을합니다. 넌 절대 안돼.

    그러나 데이터베이스의 작업을 수행하기 위해 일반적인 도우미 코드를 작성하는 경우 이러한 고려 사항은 그다지 적용되지 않습니다. 그러한 코드를 호출 할 수있는 모든 사람에게 데이터베이스의 모든 것에 대한 액세스 권한을 내재적으로 제공합니다.이것이 도우미 코드의 요점입니다. 따라서 안전 문제는 사용자 생성 데이터를 이러한 코드에서 절대 사용할 수 없도록하는 것입니다. 이것은 코딩의 일반적인 보안 문제이며 맹목적으로 bytes 와 동일한 문제입니다. 사용자 입력 문자열.을 검색어에 삽입하는 것과는 별개의 문제입니다. 사용자 입력 데이터를안전하게처리 할 수 ​​있기를 원하기 때문입니다

    . >

    따라서 나의 추천은 : 쿼리를 동적으로 어셈블하고 싶은 것은 무엇이든한다. 일반적인 python 문자열 템플릿을 테이블과 열 이름에 하위로 사용하고 where 절과 조인에 붙이고 모든 좋은 (끔찍한 디버그) 항목을 사용하십시오. 그러나 이러한 코드가 어떤 값을 사용하든 사용자가 아니라귀하가제공해야한다는 사실을 명심해야합니다 [1]. 그런 다음 SQLite의 매개 변수 대체 기능을 사용하여 사용자 입력 값을 쿼리에 값으로안전하게삽입합니다.

    [1] 만약 내가 작성한 많은 코드의 경우와 같이 사용자가 데이터베이스에 대한 모든 권한을 가진 사람들이고 그 코드가 작업을 단순화하는 것이라면이 고려 사항은 실제로 적용되지 않습니다. 아마도 사용자 지정 테이블에서 쿼리를 어셈블하고있을 것입니다. 그러나 여전히 따옴표 나 퍼센트 기호를 포함하는 피할 수없는 실제 값에서 벗어나려면 SQLite의 매개 변수 대체를 사용해야합니다.

  • 답변 # 3

    열 이름을 동적으로 지정해야한다면 확실하게 사용해야합니다. 안전하게 그렇게 할 수있는 도서관 (그리고 잘못된 것에 대해 불평하는 도서관) SQLAlchemy는 매우 능숙합니다.

    sqlite3
    
    

    psycopg2  이제동적 스키마가있는 테이블을 나타내지 만실제 데이터베이스 연결의 컨텍스트에서만 사용할 수 있습니다 (sqlalchemy는 방언과 생성 된 SQL로 수행 할 작업을 알고 있습니다). p>

    exec
    
    

    그런 다음 >>> import sqlalchemy >>> from sqlalchemy import * >>> metadata = MetaData() >>> dynamic_column = "cow" >>> foo_table = Table('foo', metadata, ... Column(dynamic_column, Integer)) >>> 를 발행 할 수 있습니다 . foo_table 와 함께 sqlalchemy는 생성 된 SQL을로그합니다. 그러나 일반적으로 sqlalchemy는 생성 된 SQL을 손에 닿지 않게합니다 (악한 목적으로 사용하는 것을 고려하지 않아도됩니다).

    >>> metadata.bind = create_engine('sqlite:///:memory:', echo=True)
    
    

    그렇습니다. sqlalchemy는 열 이름이 SQL 예약어 인 경우와 같이 특별한 처리가 필요한 열 이름을 처리합니다

    CREATE TABLE ...
    
    

    나쁜 악을 피할 수 있습니다 :

    echo=True
    
    

    (분명히 이상한 것들이 sqlite에서 완전히 유효한 식별자 임)

  • 답변 # 4

    이해해야 할먼저것은테이블 이름/컬럼 이름데이터베이스 값으로 저장된 문자열을 이스케이프 할 수있는 것과 같은 의미로 이스케이프되어야합니다.

    이유는 다음 중 하나 여야하기 때문입니다.

    잠재적 인 테이블/컬럼 이름을 수락/거부합니다. 즉, 문자열이 일부 데이터베이스에 저장 될 문자열과 반대로 허용되는 열/테이블 이름임을 보장하지 않습니다. 또는

    다이제스트를 생성하는 것과 동일한 효과를 갖는 문자열을 삭제합니다. 사용 된 함수는 궤적이 아닌 객관적입니다 (다시 한번, 일부 데이터베이스에 저장 될 문자열의 경우에는 역함). 따라서 삭제 된 이름에서 원래 이름으로 돌아가는 것을 확신 할 수있을뿐만 아니라 실수로 동일한 이름으로 두 개의 열이나 테이블을 만들려고 할 위험이 있습니다.

    두 번째로이해해야 할 것은 테이블/열 이름을 "이스케이프"하는 방법은 특정 상황에 따라 다르므로이를 수행 할 수있는 여러 가지 방법이 있다는 것입니다. 그러나 어떤 방식 으로든 sqlite에서 허용 가능한 열/테이블 이름이 무엇인지 아닌지를 정확하게 파악하기 위해 파헤쳐 야합니다.

    시작하려면 다음 조건 중 하나를 따르십시오.

    와이즈 비즈

    특정 열 이름을 사용하면 의도하지 않은 부작용이 발생할 수 있습니다.

    와이즈 비즈

    인용 된 텍스트는 모두 http://www.sqlite.org/lang_createtable.html에서 가져온 것입니다.

  • 답변 # 5

    sqlite FAQ에서 질문 24 (물론 질문의 공식화는 다음과 같은 단서를 제공하지 않습니다. 답변은 귀하의 질문에 유용 할 수 있습니다) :

    와이즈 비즈

    이름 자체에 큰 따옴표가 있으면 큰 따옴표를 다른 따옴표로 이스케이프하십시오.

    >>> foo_table.create() 2011-06-28 21:54:54,040 INFO sqlalchemy.engine.base.Engine.0x...2f4c CREATE TABLE foo ( cow INTEGER ) 2011-06-28 21:54:54,040 INFO sqlalchemy.engine.base.Engine.0x...2f4c () 2011-06-28 21:54:54,041 INFO sqlalchemy.engine.base.Engine.0x...2f4c COMMIT >>>

  • 이전 html - 창 축소시 겹치는 버튼
  • 다음 python - 인덱스가 타임 스탬프 인 경우 팬더에서 목록을 만드는 방법