简体   繁体   English

如何在原始查询中将Oracle列安全地绑定到ORDER BY到SQLAlchemy?

[英]How to safely bind Oracle column to ORDER BY to SQLAlchemy in a raw query?

I'm trying to execute a raw sql query and safely pass an order by/asc/desc based on user input. 我正在尝试执行原始sql查询,并根据用户输入安全地通过/ asc / desc传递命令。 This is the back end for a paginated datagrid. 这是分页数据网格的后端。 I cannot for the life of me figure out how to do this safely. 我一生无法解决如何安全地执行此操作。 Parameters get converted to strings so Oracle can't execute the query. 参数将转换为字符串,因此Oracle无法执行查询。 I can't find any examples of this anywhere on the internet. 我在互联网上的任何地方都找不到任何示例。 What is the best way to safely accomplish this? 安全完成此任务的最佳方法是什么? (I am not using the ORM, must be raw sql). (我没有使用ORM,必须是原始sql)。

My workaround is just setting ASC/DESC to a variable that I set. 我的解决方法是将ASC / DESC设置为我设置的变量。 This works fine and is safe. 这工作正常并且安全。 However, how do I bind a column name to the ORDER BY? 但是,如何将列名绑定到ORDER BY? Is that even possible? 那有可能吗? I can just whitelist a bunch of columns and do something similar as I do with the ASC/DESC. 我可以将一堆列列入白名单,并像使用ASC / DESC一样执行类似的操作。 I was just curious if there's a way to bind it. 我只是好奇是否有办法绑定它。 Thanks. 谢谢。

@default.route('/api/barcodes/<sort_by>/<sort_dir>', methods=['GET'])
@json_enc
def fetch_barcodes(sort_by, sort_dir):
    #time.sleep(5)

    # Can't use sort_dir as a parameter, so assign to variable to sanitize it
    ord_dir = "DESC" if sort_dir.lower() == 'desc' else 'ASC'

    records = []
    stmt = text("SELECT bb_request_id,bb_barcode,bs_status, "
        "TO_CHAR(bb_rec_cre_date, 'MM/DD/YYYY') AS bb_rec_cre_date "
        "FROM bars_barcodes,bars_status "
        "WHERE bs_status_id = bb_status_id "
        "ORDER BY :ord_by :ord_dir ")
    stmt = stmt.bindparams(ord_by=sort_by,ord_dir=ord_dir)
    rs = db.session.execute(stmt)
    records = [dict(zip(rs.keys(), row)) for row in rs]

DatabaseError: (cx_Oracle.DatabaseError) ORA-01036: illegal variable name/number [SQL: "SELECT bb_request_id,bb_barcode,bs_status, TO_CHAR(bb_rec_cre_date, 'MM/DD/YYYY') AS bb_rec_cre_date FROM bars_barcodes,bars_status WHERE bs_status_id = bb_status_id ORDER BY :ord_by :ord_dir "] [parameters: {'ord_by': u'bb_rec_cre_date', 'ord_dir': 'ASC'}] DatabaseError:(cx_Oracle.DatabaseError)ORA-01036:非法变量名称/编号[SQL:“ SELECT bb_request_id,bb_barcode,bs_status,TO_CHAR(bb_rec_cre_date,'MM / DD / YYYY')AS bb_rec_cre_date FROM bar_barcodes,bars_status DER_ID BY:ord_by:ord_dir“] [参数:{'ord_by':u'bb_rec_cre_date','ord_dir':'ASC'}]

UPDATE Solution based on accepted answer: 基于可接受答案的更新解决方案:

def fetch_barcodes(sort_by, sort_dir, page, rows_per_page):
    ord_dir_func = desc if sort_dir.lower() == 'desc' else asc
    query_limit = int(rows_per_page)
    query_offset = (int(page) - 1) * query_limit

    stmt = select([column('bb_request_id'),
                   column('bb_barcode'),
                   column('bs_status'),
                   func.to_char(column('bb_rec_cre_date'), 'MM/DD/YYYY').label('bb_rec_cre_date')]).\
        select_from(table('bars_barcode')).\
        select_from(table('bars_status')).\
        where(column('bs_status_id') == column('bb_status_id')).\
        order_by(ord_dir_func(column(sort_by))).\
        limit(query_limit).offset(query_offset)

    result = db.session.execute(stmt)
    records = [dict(row) for row in result]
    response = json_return()
    response.addRecords(records)
    #response.setTotal(len(records))
    response.setTotal(1001)
    response.setSuccess(True)
    response.addMessage("Records retrieved successfully. Limit: " + str(query_limit) + ", Offset: " + str(query_offset) + " SQL: " + str(stmt))

    return response

You could use Core constructs such as table() and column() for this instead of raw SQL strings. 您可以为此使用诸如table()column()类的Core构造,而不是原始SQL字符串。 That'd make your life easier in this regard: 这样可以使您的生活更轻松:

from sqlalchemy import select, table, column, asc, desc

ord_dir = desc if sort_dir.lower() == 'desc' else asc

stmt = select([column('bb_request_id'),
               column('bb_barcode'),
               column('bs_status'),
               func.to_char(column('bb_rec_cre_date'),
                            'MM/DD/YYYY').label('bb_rec_cre_date')]).\
    select_from(table('bars_barcodes')).\
    select_from(table('bars_status')).\
    where(column('bs_status_id') == column('bb_status_id')).\
    order_by(ord_dir(column(sort_by)))

table() and column() represent the syntactic part of a full blown Table object with Column s and can be used in this fashion for escaping purposes: table()column()表示带有Column的完整Table对象的句法部分,可以按这种方式用于转义目的:

The text handled by column() is assumed to be handled like the name of a database column; 假定由column()处理的文本与数据库列的名称一样进行处理; if the string contains mixed case, special characters, or matches a known reserved word on the target backend, the column expression will render using the quoting behavior determined by the backend. 如果字符串包含大小写混合,特殊字符或与目标后端上的已知保留字匹配,则该列表达式将使用后端确定的引用行为来呈现。

Still, whitelisting might not be a bad idea. 不过,将白名单列入可能不是一个坏主意。

Note that you don't need to manually zip() the row proxies in order to produce dictionaries. 请注意,您无需手动zip()行代理即可生成字典。 They act as mappings as is, and if you need dict() for serialization reasons or such, just do dict(row) . 它们按原样充当映射,如果出于序列化等原因需要dict() ,则只需执行dict(row)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM