বাইন্ড প্যারামিটারের পরিবর্তে মান সহ আমি আমার অ্যাপ্লিকেশনটির জন্য বৈধ এসকিউএল প্রিন্ট করতে সক্ষম হতে চাই, তবে এসকিউএলএলচেমিতে এটি কীভাবে করা যায় তা স্পষ্ট নয় (নকশার দ্বারা আমি মোটামুটি নিশ্চিত)।
কেউ কি এই সমস্যাটিকে সাধারণ উপায়ে সমাধান করেছেন?
বাইন্ড প্যারামিটারের পরিবর্তে মান সহ আমি আমার অ্যাপ্লিকেশনটির জন্য বৈধ এসকিউএল প্রিন্ট করতে সক্ষম হতে চাই, তবে এসকিউএলএলচেমিতে এটি কীভাবে করা যায় তা স্পষ্ট নয় (নকশার দ্বারা আমি মোটামুটি নিশ্চিত)।
কেউ কি এই সমস্যাটিকে সাধারণ উপায়ে সমাধান করেছেন?
উত্তর:
বেশিরভাগ ক্ষেত্রে, এসকিউএএলএলএকচেমি স্টেটমেন্ট বা ক্যোয়ারির "স্ট্রিংফিকেশন" এর মতোই সহজ:
print str(statement)
এটি একটি ওআরএম Query
পাশাপাশি যে কোনও select()
বা অন্য বিবৃতি উভয়কেই প্রযোজ্য ।
দ্রষ্টব্য : নীচের বিস্তারিত উত্তরটি স্কেলচেমি ডকুমেন্টেশনে বজায় রাখা হচ্ছে ।
কোনও নির্দিষ্ট উপভাষা বা ইঞ্জিনে সংকলিত হিসাবে বিবৃতিটি পেতে, যদি বিবৃতিটি নিজেই ইতিমধ্যে কোনওটির কাছে আবদ্ধ না হয় তবে আপনি এটি সংকলন করতে () পাঠাতে পারবেন :
print statement.compile(someengine)
বা ইঞ্জিন ছাড়া:
from sqlalchemy.dialects import postgresql
print statement.compile(dialect=postgresql.dialect())
যখন একটি ওআরএম Query
অবজেক্ট দেওয়া হয়, compile()
পদ্ধতিতে পেতে আমাদের প্রথমে কেবলমাত্র স্টেটমেন্ট অ্যাক্সেসরটি অ্যাক্সেস করতে হবে :
statement = query.statement
print statement.compile(someengine)
চূড়ান্ত স্ট্রিংয়ের সাথে আবদ্ধ পরামিতিগুলিকে "ইনলাইনড" করা উচিত বলে মূল স্টিপুলেশন সম্পর্কিত, এখানে চ্যালেঞ্জটি হ'ল এসকিউএলএলকেমি সাধারণত এটি দিয়ে কাজ করা হয় না, কারণ পাইথন ডিবিএপিআই দ্বারা এটি যথাযথভাবে পরিচালনা করা হয়, বাউন্ডিং সীমাবদ্ধ পরামিতিগুলি উল্লেখ না করা হ'ল সম্ভবত আধুনিক ওয়েব অ্যাপ্লিকেশনগুলিতে সর্বাধিক বহুল ব্যবহৃত শোনাট্য সুরক্ষা গর্ত। এসকিউএলএলচেমির নির্দিষ্ট পরিস্থিতিতে যেমন ডিডিএল নির্গত হওয়ার মতো করে এই স্ট্রিংফিকেশন করার ক্ষমতা সীমিত। এই কার্যকারিতাটি অ্যাক্সেস করতে কেউ 'আক্ষরিক_বিন্দু' পতাকা ব্যবহার করতে পারেন, এতে প্রেরণ compile_kwargs
:
from sqlalchemy.sql import table, column, select
t = table('t', column('x'))
s = select([t]).where(t.c.x == 5)
print s.compile(compile_kwargs={"literal_binds": True})
উপরোক্ত পদ্ধতির সতর্কতা রয়েছে যে এটি কেবলমাত্র মৌলিক ধরণের জন্য যেমন ইনটস এবং স্ট্রিংগুলির জন্য সমর্থিত, এবং তদতিরিক্ত, যদি bindparam
পূর্ব-সেট মান ব্যতীত যদি সরাসরি ব্যবহার করা হয়, তবে তা আর কোনওরকম শক্তিশালী করতে সক্ষম হবে না।
সমর্থিত নয় এমন প্রকারের জন্য ইনলাইন আক্ষরিক রেন্ডারিংকে সমর্থন করার TypeDecorator
জন্য, লক্ষ্য ধরণের জন্য একটি প্রয়োগ করুন যাতে একটি TypeDecorator.process_literal_param
পদ্ধতি রয়েছে
:
from sqlalchemy import TypeDecorator, Integer
class MyFancyType(TypeDecorator):
impl = Integer
def process_literal_param(self, value, dialect):
return "my_fancy_formatting(%s)" % value
from sqlalchemy import Table, Column, MetaData
tab = Table('mytable', MetaData(), Column('x', MyFancyType()))
print(
tab.select().where(tab.c.x > 5).compile(
compile_kwargs={"literal_binds": True})
)
উত্পাদন উত্পাদন যেমন:
SELECT mytable.x
FROM mytable
WHERE mytable.x > my_fancy_formatting(5)
query.prettyprint()
। এটি ডিবাগিংয়ের ব্যথাকে হ্রাস করে বড় কোয়েরিতে।
@compiles
সুন্দর-প্রিন্টিং সিস্টেমগুলি প্রয়োগ করার জন্য যে কোনও তৃতীয় পক্ষের প্যাকেজগুলির জন্য প্রচুর হুক রয়েছে (যেমন কার্সার_একসেকিউট ইভেন্ট, পাইথন লগিং ফিল্টার , ইত্যাদি)।
এটি পাইথন 2 এবং 3 এ কাজ করে এবং এটি আগের চেয়ে কিছুটা পরিষ্কার, তবে এসএ> = 1.0 প্রয়োজন।
from sqlalchemy.engine.default import DefaultDialect
from sqlalchemy.sql.sqltypes import String, DateTime, NullType
# python2/3 compatible.
PY3 = str is not bytes
text = str if PY3 else unicode
int_type = int if PY3 else (int, long)
str_type = str if PY3 else (str, unicode)
class StringLiteral(String):
"""Teach SA how to literalize various things."""
def literal_processor(self, dialect):
super_processor = super(StringLiteral, self).literal_processor(dialect)
def process(value):
if isinstance(value, int_type):
return text(value)
if not isinstance(value, str_type):
value = text(value)
result = super_processor(value)
if isinstance(result, bytes):
result = result.decode(dialect.encoding)
return result
return process
class LiteralDialect(DefaultDialect):
colspecs = {
# prevent various encoding explosions
String: StringLiteral,
# teach SA about how to literalize a datetime
DateTime: StringLiteral,
# don't format py2 long integers to NULL
NullType: StringLiteral,
}
def literalquery(statement):
"""NOTE: This is entirely insecure. DO NOT execute the resulting strings."""
import sqlalchemy.orm
if isinstance(statement, sqlalchemy.orm.Query):
statement = statement.statement
return statement.compile(
dialect=LiteralDialect(),
compile_kwargs={'literal_binds': True},
).string
ডেমো:
# coding: UTF-8
from datetime import datetime
from decimal import Decimal
from literalquery import literalquery
def test():
from sqlalchemy.sql import table, column, select
mytable = table('mytable', column('mycol'))
values = (
5,
u'snowman: ☃',
b'UTF-8 snowman: \xe2\x98\x83',
datetime.now(),
Decimal('3.14159'),
10 ** 20, # a long integer
)
statement = select([mytable]).where(mytable.c.mycol.in_(values)).limit(1)
print(literalquery(statement))
if __name__ == '__main__':
test()
এই আউটপুট দেয়: (পাইথন ২.7 এবং ৩.৪ এ পরীক্ষিত)
SELECT mytable.mycol
FROM mytable
WHERE mytable.mycol IN (5, 'snowman: ☃', 'UTF-8 snowman: ☃',
'2015-06-24 18:09:29.042517', 3.14159, 100000000000000000000)
LIMIT 1
আপনি যেটি চান তা কেবল ডিবাগ করার সময়ই বোধগম্য হয় echo=True
, আপনি সমস্ত এসকিউএল কোয়েরিগুলিতে লগ করার জন্য এসকিউএএলএলচেমি শুরু করতে পারেন । উদাহরণ স্বরূপ:
engine = create_engine(
"mysql://scott:tiger@hostname/dbname",
encoding="latin1",
echo=True,
)
এটি কেবল একটি একক অনুরোধের জন্যও সংশোধন করা যেতে পারে:
echo=False
- যদিTrue
, ইঞ্জিন সমস্ত বিবৃতি পাশাপাশিrepr()
তাদের একটি প্যারামিটার তালিকা ইঞ্জিন লগারে লগ করবে, যা ডিফল্টsys.stdout
। লগইন চালু এবং বন্ধ করতে এরecho
বৈশিষ্ট্যটিEngine
যে কোনও সময় সংশোধন করা যেতে পারে। যদি স্ট্রিংয়ে সেট করা থাকে"debug"
, ফলাফল সারিগুলি স্ট্যান্ডার্ড আউটপুটটিতেও মুদ্রিত হবে। এই পতাকাটি শেষ পর্যন্ত একটি পাইথন লগার নিয়ন্ত্রণ করে; দেখতে লগিং কনফিগার কিভাবে কনফিগার করতে লগিং সরাসরি তথ্যের জন্য।
যদি ফ্লাস্কের সাথে ব্যবহার করা হয় তবে আপনি কেবল সেট করতে পারেন
app.config["SQLALCHEMY_ECHO"] = True
একই আচরণ পেতে।
flask-sqlalchemy
গ্রহণযোগ্য উত্তর হওয়া উচিত।
আমরা এই উদ্দেশ্যে সংকলন পদ্ধতি ব্যবহার করতে পারি । ডক্স থেকে :
from sqlalchemy.sql import text
from sqlalchemy.dialects import postgresql
stmt = text("SELECT * FROM users WHERE users.name BETWEEN :x AND :y")
stmt = stmt.bindparams(x="m", y="z")
print(stmt.compile(dialect=postgresql.dialect(),compile_kwargs={"literal_binds": True}))
ফলাফল:
SELECT * FROM users WHERE users.name BETWEEN 'm' AND 'z'
ডক্স থেকে সতর্কতা:
অবিশ্বস্ত ইনপুট, যেমন ওয়েব ফর্ম বা অন্যান্য ব্যবহারকারী-ইনপুট অ্যাপ্লিকেশন থেকে প্রাপ্ত স্ট্রিং সামগ্রী সহ এই কৌশলটি কখনই ব্যবহার করবেন না। পাইথন মানগুলিকে সরাসরি এসকিউএল স্ট্রিংয়ের মানগুলিতে বাধ্য করার জন্য এসকিউএলএলচেমির সুবিধাগুলি অবিশ্বস্ত ইনপুট থেকে সুরক্ষিত নয় এবং যে ধরণের ডেটা পাস হচ্ছে তা বৈধ করে না। প্রোগ্রামিয়ালি কোনও রিলেশনাল ডাটাবেসের বিরুদ্ধে নন-ডিডিএল এসকিউএল স্টেটমেন্টের অনুরোধ করার সময় সর্বদা আবদ্ধ প্যারামিটারগুলি ব্যবহার করুন।
সুতরাং @ বুকজোরের কোডটিতে @ zzzeek এর মন্তব্যে বিল্ডিং আমি সহজেই একটি "সুন্দর-মুদ্রণযোগ্য" ক্যোয়ারী পেতে এগুলি নিয়ে এসেছি:
def prettyprintable(statement, dialect=None, reindent=True):
"""Generate an SQL expression string with bound parameters rendered inline
for the given SQLAlchemy statement. The function can also receive a
`sqlalchemy.orm.Query` object instead of statement.
can
WARNING: Should only be used for debugging. Inlining parameters is not
safe when handling user created data.
"""
import sqlparse
import sqlalchemy.orm
if isinstance(statement, sqlalchemy.orm.Query):
if dialect is None:
dialect = statement.session.get_bind().dialect
statement = statement.statement
compiled = statement.compile(dialect=dialect,
compile_kwargs={'literal_binds': True})
return sqlparse.format(str(compiled), reindent=reindent)
আমার ব্যক্তিগতভাবে একটি কঠিন কোড পড়ার কোড রয়েছে যা ইন্টেন্ট করা হয় না তাই আমি sqlparse
এসকিউএল পুনরায় পাঠাতে ব্যবহার করেছি । এটি দিয়ে ইনস্টল করা যেতে পারে pip install sqlparse
।
datatime.now()
অজগর 3 + স্ক্ল্যাচলেমি 1.0 ব্যবহার করার সময় সমস্ত মান এক ব্যতীত কাজ করে। সেই সাথে কাজ করার জন্য আপনাকে একটি কাস্টম টাইপডেকোরেটর তৈরি করার বিষয়ে @ zzzeek এর পরামর্শ অনুসরণ করতে হবে।
এই কোডটি @ বুজকরের উজ্জ্বল বিদ্যমান উত্তরের উপর ভিত্তি করে । আমি কেবল ওরাকলগুলিতে datetime.datetime
টাইপের জন্য কাস্টম রেন্ডার যুক্ত করেছি TO_DATE()
।
আপনার ডাটাবেস অনুসারে নির্দ্বিধায় কোড আপডেট করুন:
import decimal
import datetime
def printquery(statement, bind=None):
"""
print a query, with values filled in
for debugging purposes *only*
for security, you should always separate queries from their values
please also note that this function is quite slow
"""
import sqlalchemy.orm
if isinstance(statement, sqlalchemy.orm.Query):
if bind is None:
bind = statement.session.get_bind(
statement._mapper_zero_or_none()
)
statement = statement.statement
elif bind is None:
bind = statement.bind
dialect = bind.dialect
compiler = statement._compiler(dialect)
class LiteralCompiler(compiler.__class__):
def visit_bindparam(
self, bindparam, within_columns_clause=False,
literal_binds=False, **kwargs
):
return super(LiteralCompiler, self).render_literal_bindparam(
bindparam, within_columns_clause=within_columns_clause,
literal_binds=literal_binds, **kwargs
)
def render_literal_value(self, value, type_):
"""Render the value of a bind parameter as a quoted literal.
This is used for statement sections that do not accept bind paramters
on the target driver/database.
This should be implemented by subclasses using the quoting services
of the DBAPI.
"""
if isinstance(value, basestring):
value = value.replace("'", "''")
return "'%s'" % value
elif value is None:
return "NULL"
elif isinstance(value, (float, int, long)):
return repr(value)
elif isinstance(value, decimal.Decimal):
return str(value)
elif isinstance(value, datetime.datetime):
return "TO_DATE('%s','YYYY-MM-DD HH24:MI:SS')" % value.strftime("%Y-%m-%d %H:%M:%S")
else:
raise NotImplementedError(
"Don't know how to literal-quote value %r" % value)
compiler = LiteralCompiler(dialect, statement)
print compiler.process(statement)
return "%s" % value
পরিবর্তে return repr(value)
float, int- মধ্যে, দীর্ঘ অধ্যায় কারণ পাইথন যেমন জন্য longs outputting ছিল 22L
মাত্র পরিবর্তে22
"STR_TO_DATE('%s','%%Y-%%m-%%d %%H:%%M:%%S')" % value.strftime("%Y-%m-%d %H:%M:%S")
in mysql
আমি উল্লেখ করতে চাই যে উপরে বর্ণিত সমাধানগুলি তুচ্ছ তাত্পর্যপূর্ণ প্রশ্নগুলির সাথে "কেবলমাত্র কাজ করে" না। আমি যে সমস্যাটি নিয়ে এসেছি সেগুলি আরও জটিল ধরণের ছিল, যেমন pgsql এআরএআরএগুলি সমস্যা সৃষ্টি করে। আমি এমন একটি সমাধান খুঁজে পেয়েছি যা আমার জন্য, এমনকি pgsql ARRAYs দিয়েও কাজ করেছিল:
থেকে ধার করা: https://gist.github.com/gsakkis/4572159
লিঙ্কযুক্ত কোডটি এসকিউএএলএলচেমির পুরানো সংস্করণে ভিত্তি করে বলে মনে হচ্ছে। _Mapper_zero_or_none বৈশিষ্ট্যটি নেই বলে আপনি একটি ত্রুটি পাবেন। এখানে একটি আপডেট হওয়া সংস্করণ যা নতুন সংস্করণে কাজ করবে, আপনি কেবলমাত্র _ ম্যাপার_জার_অর_নোনটি বাইন্ডের সাথে প্রতিস্থাপন করবেন। অতিরিক্তভাবে, এর মধ্যে pgsql অ্যারে সমর্থন রয়েছে:
# adapted from:
# https://gist.github.com/gsakkis/4572159
from datetime import date, timedelta
from datetime import datetime
from sqlalchemy.orm import Query
try:
basestring
except NameError:
basestring = str
def render_query(statement, dialect=None):
"""
Generate an SQL expression string with bound parameters rendered inline
for the given SQLAlchemy statement.
WARNING: This method of escaping is insecure, incomplete, and for debugging
purposes only. Executing SQL statements with inline-rendered user values is
extremely insecure.
Based on http://stackoverflow.com/questions/5631078/sqlalchemy-print-the-actual-query
"""
if isinstance(statement, Query):
if dialect is None:
dialect = statement.session.bind.dialect
statement = statement.statement
elif dialect is None:
dialect = statement.bind.dialect
class LiteralCompiler(dialect.statement_compiler):
def visit_bindparam(self, bindparam, within_columns_clause=False,
literal_binds=False, **kwargs):
return self.render_literal_value(bindparam.value, bindparam.type)
def render_array_value(self, val, item_type):
if isinstance(val, list):
return "{%s}" % ",".join([self.render_array_value(x, item_type) for x in val])
return self.render_literal_value(val, item_type)
def render_literal_value(self, value, type_):
if isinstance(value, long):
return str(value)
elif isinstance(value, (basestring, date, datetime, timedelta)):
return "'%s'" % str(value).replace("'", "''")
elif isinstance(value, list):
return "'{%s}'" % (",".join([self.render_array_value(x, type_.item_type) for x in value]))
return super(LiteralCompiler, self).render_literal_value(value, type_)
return LiteralCompiler(dialect, statement).process(statement)
নেস্টেড অ্যারেগুলির দুটি স্তরের পরীক্ষিত।
from file import render_query; print(render_query(query))
sqlalchemy.engine
লগ এ আলতো চাপ দিয়ে একটি কম ভঙ্গুর সমাধান তৈরি করতে পারেন । এটি অনুসন্ধানগুলিকে লগ করে এবং প্যারামিটারগুলিকে আবদ্ধ করে, আপনাকে কেবল সহজেই নির্মিত এসকিউএল কোয়েরি স্ট্রিংয়ের মানগুলির সাথে বাইন্ড স্থানধারককে প্রতিস্থাপন করতে হবে।