diff --git a/lib/extras.py b/lib/extras.py index 332b08f522ad778976f5cb49cace0f51e6ea6276..9f3644db3f759acfe1fe6806f758bd15d36e4c7a 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -1142,7 +1142,7 @@ def register_composite(name, conn_or_curs, globally=False, factory=None): return caster -def _paginate(seq, page_size): +def _paginate(seq, page_size, to_byte=False): """Consume an iterable and return it in chunks. Every chunk is at most `page_size`. Never return an empty chunk. @@ -1152,7 +1152,16 @@ def _paginate(seq, page_size): while True: try: for i in range(page_size): - page.append(next(it)) + if not to_byte: + page.append(next(it)) + continue + vs = next(it) + if isinstance(vs, (list, tuple)): + # Ignore None object + # Serialized params to bytes + page.append(list(map(lambda v: v if v is None else str(v).encode('utf-8'), vs))) + else: + page.append(vs) yield page page = [] except StopIteration: @@ -1308,16 +1317,17 @@ def execute_prepared_batch(cur, prepared_statement_name, args_list, page_size=10 r""" [openGauss libpq only] - Execute prepared statement with api `PQexecPreparedBatch` (new api in openGauss) + Execute prepared statement with api `PQexecPreparedBatch` (new api in openGauss's libpq.so) - Param: - argslist: 2d list, do nothing if empty + Arguments: + argslist: Two-dimensional list, if empty, return directly + Each parameter in the argument list must be a string or be string-able(should implements `__str__` magic method) """ if len(args_list) == 0: return nparams = len(args_list[0]) - for page in _paginate(args_list, page_size=page_size): + for page in _paginate(args_list, page_size=page_size, to_byte=True): cur.execute_prepared_batch(prepared_statement_name, nparams, len(page), page) @@ -1325,14 +1335,15 @@ def execute_params_batch(cur, sql_format, args_list, page_size=100): r""" [openGauss libpq only] - Execute sql with api `PQexecParamsBatch` (new api in openGauss) + Execute sql with api `PQexecParamsBatch` (new api in openGauss's libpq.so) Arguments: - argslist: 2d list, do nothing if empty + argslist: Two-dimensional list, if empty, return directly + Each parameter in the argument list must be a string or be string-able(should implements `__str__` magic method) """ if len(args_list) == 0: return nparams = len(args_list[0]) - for page in _paginate(args_list, page_size=page_size): + for page in _paginate(args_list, page_size=page_size, to_byte=True): cur.execute_params_batch(sql_format, nparams, len(page), page) diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c index 17ad401104cc4775068bad3952c0f131677f56da..62a5d55c3807f2a9577dea965b610ebfd8cddd5f 100644 --- a/psycopg/cursor_type.c +++ b/psycopg/cursor_type.c @@ -628,7 +628,7 @@ curs_execute_prepared_batch(cursorObject *self, PyObject *args) int nParams = 0, nBatch = 0; PyObject *argsList = NULL; - Py_ssize_t rowIdx, colIdx, total; + int rowIdx, colIdx, total; char **paramValues = NULL; PGresult *res = NULL; @@ -642,7 +642,7 @@ curs_execute_prepared_batch(cursorObject *self, PyObject *args) } Dprintf("execute_prepared_batch parsed statement_name: %s, nParams: %d, nBatch: %d", stmtName, nParams, nBatch); - total = nBatch*nParams; + total = nBatch * nParams; EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_ASYNC(self, execute_prepared_batch); @@ -679,11 +679,12 @@ curs_execute_prepared_batch(cursorObject *self, PyObject *args) PyObject *argItem = PySequence_GetItem(rowArgs, colIdx); if (argItem == Py_None) { - paramValues[rowIdx*nParams+colIdx] = "NULL"; + paramValues[rowIdx * nParams + colIdx] = NULL; } else { - PyObject *t = microprotocol_getquoted(argItem, self->conn); - paramValues[rowIdx*nParams+colIdx] = strdup(Bytes_AsString(t)); - Py_XDECREF(t); + if (!(argItem = psyco_ensure_bytes(argItem))) { + goto exit; + } + paramValues[rowIdx * nParams + colIdx] = Bytes_AsString(argItem); } Py_XDECREF(argItem); } @@ -715,7 +716,7 @@ curs_execute_params_batch(cursorObject *self, PyObject *args) int nParams = 0, nBatch = 0; PyObject *argsList = NULL; - Py_ssize_t rowIdx, colIdx, total; + int rowIdx, colIdx, total; char **paramValues = NULL; PGresult *res = NULL; @@ -729,7 +730,7 @@ curs_execute_params_batch(cursorObject *self, PyObject *args) Dprintf("execute_params_batch parsed sql: %s, nParams: %d, nBatch: %d", sql, nParams, nBatch); - total = nBatch*nParams; + total = nBatch * nParams; EXC_IF_CURS_CLOSED(self); EXC_IF_CURS_ASYNC(self, execute_params_batch); @@ -765,11 +766,12 @@ curs_execute_params_batch(cursorObject *self, PyObject *args) PyObject *argItem = PySequence_GetItem(rowArgs, colIdx); if (argItem == Py_None) { - paramValues[rowIdx*nParams+colIdx] = "NULL"; + paramValues[rowIdx * nParams + colIdx] = NULL; } else { - PyObject *t = microprotocol_getquoted(argItem, self->conn); - paramValues[rowIdx*nParams+colIdx] = strdup(Bytes_AsString(t)); - Py_XDECREF(t); + if (!(argItem = psyco_ensure_bytes(argItem))) { + goto exit; + } + paramValues[rowIdx * nParams + colIdx] = Bytes_AsString(argItem); } Py_XDECREF(argItem); }